mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2026-01-23 00:24:26 +01:00
Storage permission no longer used
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user