Storage permission no longer used

This commit is contained in:
Aleksander Nowakowski
2020-02-03 12:06:15 +01:00
parent 26d218e551
commit 1dec23f5ca
23 changed files with 129 additions and 7260 deletions

View File

@@ -1,78 +0,0 @@
/*
* Copyright (c) 2015, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package no.nordicsemi.android.nrftoolbox;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
public class PermissionRationaleFragment extends DialogFragment {
private static final String ARG_PERMISSION = "ARG_PERMISSION";
private static final String ARG_TEXT = "ARG_TEXT";
private PermissionDialogListener listener;
public interface PermissionDialogListener {
void onRequestPermission(final String permission);
}
@Override
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
if (context instanceof PermissionDialogListener) {
listener = (PermissionDialogListener) context;
} else {
throw new IllegalArgumentException("The parent activity must implement PermissionDialogListener");
}
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
public static PermissionRationaleFragment getInstance(final int aboutResId, final String permission) {
final PermissionRationaleFragment fragment = new PermissionRationaleFragment();
final Bundle args = new Bundle();
args.putInt(ARG_TEXT, aboutResId);
args.putString(ARG_PERMISSION, permission);
fragment.setArguments(args);
return fragment;
}
@Override
@NonNull
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = requireArguments();
final StringBuilder text = new StringBuilder(getString(args.getInt(ARG_TEXT)));
return new AlertDialog.Builder(requireContext()).setTitle(R.string.permission_title).setMessage(text)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, (dialog, which) -> listener.onRequestPermission(args.getString(ARG_PERMISSION))).create();
}
}

View File

@@ -21,14 +21,12 @@
*/
package no.nordicsemi.android.nrftoolbox.dfu;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.NotificationManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
@@ -42,13 +40,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.DialogFragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
@@ -62,12 +53,16 @@ import android.widget.Toast;
import java.io.File;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import no.nordicsemi.android.dfu.DfuProgressListener;
import no.nordicsemi.android.dfu.DfuProgressListenerAdapter;
import no.nordicsemi.android.dfu.DfuServiceInitiator;
import no.nordicsemi.android.dfu.DfuServiceListenerHelper;
import no.nordicsemi.android.nrftoolbox.AppHelpFragment;
import no.nordicsemi.android.nrftoolbox.PermissionRationaleFragment;
import no.nordicsemi.android.nrftoolbox.R;
import no.nordicsemi.android.nrftoolbox.dfu.adapter.FileBrowserAppsAdapter;
import no.nordicsemi.android.nrftoolbox.dfu.fragment.UploadCancelFragment;
@@ -75,15 +70,14 @@ import no.nordicsemi.android.nrftoolbox.dfu.fragment.ZipInfoFragment;
import no.nordicsemi.android.nrftoolbox.dfu.settings.SettingsActivity;
import no.nordicsemi.android.nrftoolbox.dfu.settings.SettingsFragment;
import no.nordicsemi.android.nrftoolbox.scanner.ScannerFragment;
import no.nordicsemi.android.nrftoolbox.utility.FileHelper;
/**
* DfuActivity is the main DFU activity It implements DFUManagerCallbacks to receive callbacks from DFUManager class It implements
* DeviceScannerFragment.OnDeviceSelectedListener callback to receive callback when device is selected from scanning dialog The activity supports portrait and
* DfuActivity is the main DFU activity It implements DFUManagerCallbacks to receive callbacks from
* DfuManager class It implements DeviceScannerFragment.OnDeviceSelectedListener callback to receive callback when device is selected from scanning dialog The activity supports portrait and
* landscape orientations
*/
public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cursor>, ScannerFragment.OnDeviceSelectedListener,
UploadCancelFragment.CancelFragmentListener, PermissionRationaleFragment.PermissionDialogListener {
UploadCancelFragment.CancelFragmentListener {
private static final String TAG = "DfuActivity";
private static final String PREFS_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_DEVICE_NAME";
@@ -106,7 +100,6 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
private static final String EXTRA_URI = "uri";
private static final int PERMISSION_REQ = 25;
private static final int ENABLE_BT_REQ = 0;
private static final int SELECT_FILE_REQ = 1;
private static final int SELECT_INIT_FILE_REQ = 2;
@@ -248,16 +241,6 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
}
setGUI();
// Try to create sample files
if (FileHelper.newSamplesAvailable(this)) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
FileHelper.createSamples(this);
} else {
final DialogFragment dialog = PermissionRationaleFragment.getInstance(R.string.permission_sd_text, Manifest.permission.WRITE_EXTERNAL_STORAGE);
dialog.show(getSupportFragmentManager(), null);
}
}
// restore saved state
fileType = DfuService.TYPE_AUTO; // Default
if (savedInstanceState != null) {
@@ -355,27 +338,6 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
resumed = false;
}
@Override
public void onRequestPermission(final String permission) {
ActivityCompat.requestPermissions(this, new String[] { permission }, PERMISSION_REQ);
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQ: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We have been granted the Manifest.permission.WRITE_EXTERNAL_STORAGE permission. Now we may proceed with exporting.
FileHelper.createSamples(this);
} else {
Toast.makeText(this, R.string.no_required_permission, Toast.LENGTH_SHORT).show();
}
break;
}
}
}
private void isBLESupported() {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
showToast(R.string.no_ble);
@@ -384,8 +346,7 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
}
private boolean isBLEEnabled() {
final BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter adapter = manager.getAdapter();
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
return adapter != null && adapter.isEnabled();
}
@@ -425,6 +386,7 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK)
return;
@@ -437,10 +399,10 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
// and read new one
final Uri uri = data.getData();
/*
* The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
* directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
*/
/*
* The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
* directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
*/
if (uri.getScheme().equals("file")) {
// the direct path to the file has been returned
final String path = uri.getPath();
@@ -470,10 +432,10 @@ public class DfuActivity extends AppCompatActivity implements LoaderCallbacks<Cu
// and read new one
final Uri uri = data.getData();
/*
* The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
* directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
*/
/*
* The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
* directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
*/
if (uri.getScheme().equals("file")) {
// the direct path to the file has been returned
initFilePath = uri.getPath();

View File

@@ -82,6 +82,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.util.UUID;
@@ -113,7 +114,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
private final static String SIS_EDIT_MODE = "sis_edit_mode";
private final static int SELECT_FILE_REQ = 2678; // random
private final static int PERMISSION_REQ = 24; // random, 8-bit
private final static int REQUEST_SAVE = 2679;
UARTConfigurationSynchronizer wearableSynchronizer;
@@ -364,11 +365,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
return true;
}
case R.id.action_export: {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
exportConfiguration();
} else {
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSION_REQ);
}
exportConfiguration();
return true;
}
case R.id.action_rename: {
@@ -405,22 +402,6 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
return false;
}
@Override
public void onRequestPermissionsResult(final int requestCode, final @NonNull String[] permissions, final @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQ: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We have been granted the Manifest.permission.WRITE_EXTERNAL_STORAGE permission. Now we may proceed with exporting.
exportConfiguration();
} else {
Toast.makeText(this, R.string.no_required_permission, Toast.LENGTH_SHORT).show();
}
break;
}
}
}
@Override
public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) {
if (position > 0) { // FIXME this is called twice after rotation.
@@ -441,7 +422,14 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
else
message = "Unknown error";
final String msg = message;
Snackbar.make(container, R.string.uart_configuration_loading_failed, Snackbar.LENGTH_INDEFINITE).setAction(R.string.uart_action_details, v -> new AlertDialog.Builder(UARTActivity.this).setMessage(msg).setTitle(R.string.uart_action_details).setPositiveButton(R.string.ok, null).show()).show();
Snackbar.make(container, R.string.uart_configuration_loading_failed, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.uart_action_details, v ->
new AlertDialog.Builder(UARTActivity.this)
.setMessage(msg)
.setTitle(R.string.uart_action_details)
.setPositiveButton(R.string.ok, null)
.show())
.show();
return;
}
@@ -484,14 +472,19 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
appsList.setAdapter(new FileBrowserAppsAdapter(this));
appsList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
appsList.setItemChecked(0, true);
new AlertDialog.Builder(this).setTitle(R.string.dfu_alert_no_filebrowser_title).setView(customView).setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss()).setPositiveButton(R.string.yes, (dialog, which) -> {
final int pos = appsList.getCheckedItemPosition();
if (pos >= 0) {
final String query = getResources().getStringArray(R.array.dfu_app_file_browser_action)[pos];
final Intent storeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(query));
startActivity(storeIntent);
}
}).show();
new AlertDialog.Builder(this)
.setTitle(R.string.dfu_alert_no_filebrowser_title)
.setView(customView)
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.setPositiveButton(R.string.yes, (dialog, which) -> {
final int pos = appsList.getCheckedItemPosition();
if (pos >= 0) {
final String query = getResources().getStringArray(R.array.dfu_app_file_browser_action)[pos];
final Intent storeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(query));
startActivity(storeIntent);
}
})
.show();
}
}
@@ -499,17 +492,18 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_CANCELED)
Uri uri = data != null ? data.getData() : null;
if (resultCode == Activity.RESULT_CANCELED || uri == null)
return;
switch (requestCode) {
case SELECT_FILE_REQ: {
// clear previous data
final Uri uri = data.getData();
/*
* The URI returned from application may be in 'file' or 'content' schema.
* 'File' schema allows us to create a File object and read details from if directly.
* Data from 'Content' schema must be read with use of a Content Provider. To do that we are using a Loader.
* Data from 'Content' schema must be read with use of a Content Provider.
* To do that we are using a Loader.
*/
if (uri.getScheme().equals("file")) {
// The direct path to the file has been returned
@@ -538,6 +532,19 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
}
break;
}
case REQUEST_SAVE: {
try {
final OutputStream stream = getContentResolver().openOutputStream(uri);
final OutputStreamWriter writer = new OutputStreamWriter(stream);
writer.append(databaseHelper.getConfiguration(configurationSpinner.getSelectedItemId()));
writer.close();
Toast.makeText(this, R.string.uart_configuration_export_succeeded, Toast.LENGTH_SHORT).show();
} catch (final Exception e) {
Log.e(TAG, "Error while exporting server configuration", e);
Toast.makeText(this, R.string.uart_configuration_save_error, Toast.LENGTH_SHORT).show();
}
break;
}
}
}
@@ -694,7 +701,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
* Loads the configuration from the given input stream.
* @param is the input stream
*/
private void loadConfiguration(final InputStream is) {
private void loadConfiguration(@NonNull final InputStream is) {
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
final StringBuilder builder = new StringBuilder();
@@ -727,40 +734,62 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
else
message = "Unknown error";
final String msg = message;
Snackbar.make(container, R.string.uart_configuration_loading_failed, Snackbar.LENGTH_INDEFINITE).setAction(R.string.uart_action_details, v -> new AlertDialog.Builder(UARTActivity.this).setMessage(msg).setTitle(R.string.uart_action_details).setPositiveButton(R.string.ok, null).show()).show();
Snackbar.make(container, R.string.uart_configuration_loading_failed, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.uart_action_details, v ->
new AlertDialog.Builder(UARTActivity.this)
.setMessage(msg)
.setTitle(R.string.uart_action_details)
.setPositiveButton(R.string.ok, null)
.show())
.show();
}
}
private void exportConfiguration() {
// TODO this may not work if the SD card is not available. (Lenovo A806, email from 11.03.2015)
final File folder = new File(Environment.getExternalStorageDirectory(), FileHelper.NORDIC_FOLDER);
if (!folder.exists())
folder.mkdir();
final File serverFolder = new File(folder, FileHelper.UART_FOLDER);
if (!serverFolder.exists())
serverFolder.mkdir();
final String fileName = configuration.getName() + ".xml";
final File file = new File(serverFolder, fileName);
try {
file.createNewFile();
final FileOutputStream fos = new FileOutputStream(file);
final OutputStreamWriter writer = new OutputStreamWriter(fos);
writer.append(databaseHelper.getConfiguration(configurationSpinner.getSelectedItemId()));
writer.close();
// Notify user about the file
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileHelper.getContentUri(this, file), "text/xml");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, 420, intent, 0);
final Notification notification = new NotificationCompat.Builder(this, ToolboxApplication.FILE_SAVED_CHANNEL).setContentIntent(pendingIntent).setContentTitle(fileName).setContentText(getText(R.string.uart_configuration_export_succeeded))
.setAutoCancel(true).setShowWhen(true).setTicker(getText(R.string.uart_configuration_export_succeeded_ticker)).setSmallIcon(android.R.drawable.stat_notify_sdcard).build();
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(fileName, 823, notification);
} catch (final Exception e) {
Log.e(TAG, "Error while exporting configuration", e);
Toast.makeText(this, R.string.uart_configuration_save_error, Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/xml");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, REQUEST_SAVE);
} else {
final File folder = new File(Environment.getExternalStorageDirectory(), FileHelper.NORDIC_FOLDER);
if (!folder.exists())
folder.mkdir();
final File serverFolder = new File(folder, FileHelper.UART_FOLDER);
if (!serverFolder.exists())
serverFolder.mkdir();
final File file = new File(serverFolder, fileName);
try {
file.createNewFile();
final FileOutputStream fos = new FileOutputStream(file);
final OutputStreamWriter writer = new OutputStreamWriter(fos);
writer.append(databaseHelper.getConfiguration(configurationSpinner.getSelectedItemId()));
writer.close();
// Notify user about the file
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileHelper.getContentUri(this, file), "text/xml");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, 420, intent, 0);
final Notification notification = new NotificationCompat.Builder(this, ToolboxApplication.FILE_SAVED_CHANNEL)
.setContentIntent(pendingIntent)
.setContentTitle(fileName)
.setContentText(getText(R.string.uart_configuration_export_succeeded))
.setAutoCancel(true)
.setShowWhen(true)
.setTicker(getText(R.string.uart_configuration_export_succeeded_ticker))
.setSmallIcon(android.R.drawable.stat_notify_sdcard)
.build();
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(fileName, 823, notification);
} catch (final Exception e) {
Log.e(TAG, "Error while exporting configuration", e);
Toast.makeText(this, R.string.uart_configuration_save_error, Toast.LENGTH_SHORT).show();
}
}
}
@@ -768,7 +797,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
* Converts the old configuration, stored in preferences, into the first XML configuration and saves it to the database.
* If there is already any configuration in the database this method does nothing.
*/
private void ensureFirstConfiguration(final DatabaseHelper databaseHelper) {
private void ensureFirstConfiguration(@NonNull final DatabaseHelper databaseHelper) {
// This method ensures that the "old", single configuration has been saved to the database.
if (databaseHelper.getConfigurationsCount() == 0) {
final UartConfiguration configuration = new UartConfiguration();
@@ -807,16 +836,16 @@ public class UARTActivity extends BleProfileServiceReadyActivity<UARTService.UAR
*/
private class CommentVisitor implements Visitor {
@Override
public void read(final Type type, final NodeMap<InputNode> node) throws Exception {
public void read(final Type type, final NodeMap<InputNode> node) {
// do nothing
}
@Override
public void write(final Type type, final NodeMap<OutputNode> node) throws Exception {
public void write(final Type type, final NodeMap<OutputNode> node) {
if (type.getType().equals(Command[].class)) {
OutputNode element = node.getNode();
final OutputNode element = node.getNode();
StringBuilder builder = new StringBuilder("A configuration must have 9 commands, one for each button.\n Possible icons are:");
final StringBuilder builder = new StringBuilder("A configuration must have 9 commands, one for each button.\n Possible icons are:");
for (Command.Icon icon : Command.Icon.values())
builder.append("\n - ").append(icon.toString());
element.setComment(builder.toString());

View File

@@ -24,209 +24,31 @@ package no.nordicsemi.android.nrftoolbox.utility;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import no.nordicsemi.android.nrftoolbox.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class FileHelper {
private static final String TAG = "FileHelper";
private static final String PREFS_SAMPLES_VERSION = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_SAMPLES_VERSION";
private static final int CURRENT_SAMPLES_VERSION = 4;
public static final String NORDIC_FOLDER = "Nordic Semiconductor";
public static final String UART_FOLDER = "UART Configurations";
public static final String BOARD_FOLDER = "Board";
public static final String BOARD_NRF6310_FOLDER = "nrf6310";
public static final String BOARD_PCA10028_FOLDER = "pca10028";
public static final String BOARD_PCA10036_FOLDER = "pca10036";
public static boolean newSamplesAvailable(final Context context) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final int version = preferences.getInt(PREFS_SAMPLES_VERSION, 0);
return version < CURRENT_SAMPLES_VERSION;
}
public static void createSamples(final Context context) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final int version = preferences.getInt(PREFS_SAMPLES_VERSION, 0);
if (version == CURRENT_SAMPLES_VERSION)
return;
/*
* Copy example HEX files to the external storage. Files will be copied if the DFU Applications folder is missing
*/
final File root = new File(Environment.getExternalStorageDirectory(), "Nordic Semiconductor");
if (!root.exists()) {
root.mkdir();
}
final File board = new File(root, "Board");
if (!board.exists()) {
board.mkdir();
}
final File nrf6310 = new File(board, "nrf6310");
if (!nrf6310.exists()) {
nrf6310.mkdir();
}
final File pca10028 = new File(board, "pca10028");
if (!pca10028.exists()) {
pca10028.mkdir();
}
// Remove old files. Those will be moved to a new folder structure
new File(root, "ble_app_hrs_s110_v6_0_0.hex").delete();
new File(root, "ble_app_rscs_s110_v6_0_0.hex").delete();
new File(root, "ble_app_hrs_s110_v7_0_0.hex").delete();
new File(root, "ble_app_rscs_s110_v7_0_0.hex").delete();
new File(root, "blinky_arm_s110_v7_0_0.hex").delete();
new File(root, "dfu_2_0.bat").delete(); // This file has been migrated to 3.0
new File(root, "dfu_3_0.bat").delete(); // This file has been migrated to 3.1
new File(root, "dfu_2_0.sh").delete(); // This file has been migrated to 3.0
new File(root, "dfu_3_0.sh").delete(); // This file has been migrated to 3.1
new File(root, "README.txt").delete(); // This file has been modified to match v.3.0+
boolean oldCopied = false;
boolean newCopied = false;
// nrf6310 files
File f = new File(nrf6310, "ble_app_hrs_s110_v6_0_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_s110_v6_0_0, f);
oldCopied = true;
}
f = new File(nrf6310, "ble_app_rscs_s110_v6_0_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_rscs_s110_v6_0_0, f);
oldCopied = true;
}
f = new File(nrf6310, "ble_app_hrs_s110_v7_0_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_s110_v7_0_0, f);
oldCopied = true;
}
f = new File(nrf6310, "ble_app_rscs_s110_v7_0_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_rscs_s110_v7_0_0, f);
oldCopied = true;
}
f = new File(nrf6310, "blinky_arm_s110_v7_0_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.blinky_arm_s110_v7_0_0, f);
oldCopied = true;
}
// PCA10028 files
f = new File(pca10028, "blinky_s110_v7_1_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.blinky_s110_v7_1_0, f);
oldCopied = true;
}
f = new File(pca10028, "blinky_s110_v7_1_0_ext_init.dat");
if (!f.exists()) {
copyRawResource(context, R.raw.blinky_s110_v7_1_0_ext_init, f);
oldCopied = true;
}
f = new File(pca10028, "ble_app_hrs_dfu_s110_v7_1_0.hex");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_dfu_s110_v7_1_0, f);
oldCopied = true;
}
f = new File(pca10028, "ble_app_hrs_dfu_s110_v7_1_0_ext_init.dat");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_dfu_s110_v7_1_0_ext_init, f);
oldCopied = true;
}
new File(root, "ble_app_hrs_dfu_s110_v8_0_0.zip").delete(); // name changed
f = new File(pca10028, "ble_app_hrs_dfu_s110_v8_0_0_sdk_v8_0.zip");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_dfu_s110_v8_0_0_sdk_v8_0, f);
newCopied = true;
}
f = new File(pca10028, "ble_app_hrs_dfu_s110_v8_0_0_sdk_v9_0.zip");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_dfu_s110_v8_0_0_sdk_v9_0, f);
newCopied = true;
}
f = new File(pca10028, "ble_app_hrs_dfu_all_in_one_sdk_v9_0.zip");
if (!f.exists()) {
copyRawResource(context, R.raw.ble_app_hrs_dfu_all_in_one_sdk_v9_0, f);
newCopied = true;
}
if (oldCopied)
Toast.makeText(context, R.string.dfu_example_files_created, Toast.LENGTH_SHORT).show();
else if (newCopied)
Toast.makeText(context, R.string.dfu_example_new_files_created, Toast.LENGTH_SHORT).show();
// Scripts
newCopied = false;
f = new File(root, "dfu_3_1.bat");
if (!f.exists()) {
copyRawResource(context, R.raw.dfu_win_3_1, f);
newCopied = true;
}
f = new File(root, "dfu_3_1.sh");
if (!f.exists()) {
copyRawResource(context, R.raw.dfu_mac_3_1, f);
newCopied = true;
}
f = new File(root, "README.txt");
if (!f.exists()) {
copyRawResource(context, R.raw.readme, f);
}
if (newCopied)
Toast.makeText(context, R.string.dfu_scripts_created, Toast.LENGTH_SHORT).show();
// Save the current version
preferences.edit().putInt(PREFS_SAMPLES_VERSION, CURRENT_SAMPLES_VERSION).apply();
}
/**
* Copies the file from res/raw with given id to given destination file. If dest does not exist it will be created.
*
* @param context activity context
* @param rawResId the resource id
* @param dest destination file
*/
private static void copyRawResource(final Context context, final int rawResId, final File dest) {
try {
final InputStream is = context.getResources().openRawResource(rawResId);
final FileOutputStream fos = new FileOutputStream(dest);
final byte[] buf = new byte[1024];
int read;
try {
while ((read = is.read(buf)) > 0)
fos.write(buf, 0, read);
} finally {
is.close();
fos.close();
}
} catch (final IOException e) {
DebugLogger.e(TAG, "Error while copying HEX file " + e.toString());
}
}
public static Uri getContentUri(final Context context, final File file) {
@Nullable
public static Uri getContentUri(@NonNull final Context context, @NonNull final File file) {
final String filePath = file.getAbsolutePath();
final Uri uri = MediaStore.Files.getContentUri("external");
final Cursor cursor = context.getContentResolver().query(
try (final Cursor cursor = context.getContentResolver().query(
uri,
new String[]{BaseColumns._ID},
new String[] { BaseColumns._ID },
MediaStore.Files.FileColumns.DATA + "=? ",
new String[]{filePath}, null);
try {
new String[] { filePath },
null)) {
if (cursor != null && cursor.moveToFirst()) {
final int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
return Uri.withAppendedPath(uri, String.valueOf(id));
@@ -239,8 +61,6 @@ public class FileHelper {
return null;
}
}
} finally {
cursor.close();
}
}
}