diff --git a/sample/src/main/java/io/runtime/mcumgr/sample/dialog/YesNoDialogFragment.java b/sample/src/main/java/io/runtime/mcumgr/sample/dialog/YesNoDialogFragment.java new file mode 100644 index 00000000..77dc8fb3 --- /dev/null +++ b/sample/src/main/java/io/runtime/mcumgr/sample/dialog/YesNoDialogFragment.java @@ -0,0 +1,55 @@ +package io.runtime.mcumgr.sample.dialog; + +import android.app.Dialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; + +public class YesNoDialogFragment extends DialogFragment { + private static final String ARG_REQUEST_ID = "requestId"; + private static final String ARG_TITLE_ID = "titleId"; + private static final String ARG_QUESTION_ID = "questionId"; + + public interface Listener { + void onAnswer(final int requestId, final boolean yes); + } + + public static DialogFragment getInstance(final int requestId, + @StringRes final int titleId, @StringRes final int questionId) { + final DialogFragment fragment = new YesNoDialogFragment(); + + final Bundle args = new Bundle(); + args.putInt(ARG_REQUEST_ID, requestId); + args.putInt(ARG_TITLE_ID, titleId); + args.putInt(ARG_QUESTION_ID, questionId); + fragment.setArguments(args); + + return fragment; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { + final Bundle args = requireArguments(); + final int requestId = args.getInt(ARG_REQUEST_ID); + + return new AlertDialog.Builder(requireContext()) + .setTitle(args.getInt(ARG_TITLE_ID)) + .setMessage(args.getInt(ARG_QUESTION_ID)) + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + final Listener listener = (Listener) getParentFragment(); + if (listener != null) + listener.onAnswer(requestId, true); + }) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + final Listener listener = (Listener) getParentFragment(); + if (listener != null) + listener.onAnswer(requestId, false); + }) + .create(); + } +} diff --git a/sample/src/main/java/io/runtime/mcumgr/sample/fragment/mcumgr/ImageUploadFragment.java b/sample/src/main/java/io/runtime/mcumgr/sample/fragment/mcumgr/ImageUploadFragment.java index 545afa04..c5c290bb 100644 --- a/sample/src/main/java/io/runtime/mcumgr/sample/fragment/mcumgr/ImageUploadFragment.java +++ b/sample/src/main/java/io/runtime/mcumgr/sample/fragment/mcumgr/ImageUploadFragment.java @@ -32,12 +32,15 @@ import io.runtime.mcumgr.sample.di.Injectable; import io.runtime.mcumgr.sample.dialog.HelpDialogFragment; import io.runtime.mcumgr.sample.dialog.SelectImageDialogFragment; +import io.runtime.mcumgr.sample.dialog.YesNoDialogFragment; import io.runtime.mcumgr.sample.utils.StringUtils; import io.runtime.mcumgr.sample.viewmodel.mcumgr.ImageUploadViewModel; import io.runtime.mcumgr.sample.viewmodel.mcumgr.McuMgrViewModelFactory; -public class ImageUploadFragment extends FileBrowserFragment implements Injectable, SelectImageDialogFragment.OnImageSelectedListener { +public class ImageUploadFragment extends FileBrowserFragment implements Injectable, + SelectImageDialogFragment.OnImageSelectedListener, YesNoDialogFragment.Listener { private static final int REQUEST_UPLOAD = 0; + private static final int REQUEST_OVERWRITE = 1; @Inject McuMgrViewModelFactory viewModelFactory; @@ -127,6 +130,12 @@ public void onViewCreated(@NonNull final View view, @Nullable final Bundle saved binding.actionCancel.setVisibility(View.GONE); binding.actionPauseResume.setVisibility(View.GONE); }); + viewModel.getHashAlreadyFoundEvent().observe(getViewLifecycleOwner(), isActive -> { + final DialogFragment dialog = YesNoDialogFragment + .getInstance(REQUEST_OVERWRITE, R.string.image_overwrite_title, + isActive ? R.string.image_overwrite_active_message : R.string.image_overwrite_message); + dialog.show(getChildFragmentManager(), null); + }); viewModel.getBusyState().observe(getViewLifecycleOwner(), busy -> { binding.actionSelectFile.setEnabled(!busy); binding.actionUpload.setEnabled(isFileLoaded() && !busy); @@ -208,7 +217,21 @@ protected void onFileLoadingFailed(final int error) { public void onImageSelected(final int requestId, final int image) { final byte[] data = getFileContent(); if (data != null) { - viewModel.upload(data, image); + viewModel.upload(data, image, false); + } + } + + @Override + public void onAnswer(int requestId, boolean yes) { + if (requestId == REQUEST_OVERWRITE) { + final byte[] data = getFileContent(); + if (data != null) { + if (yes) { + viewModel.upload(data, 0, true); + } else { + viewModel.onUploadCanceled(); + } + } } } diff --git a/sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/ImageUploadViewModel.java b/sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/ImageUploadViewModel.java index d5f21436..3ce579ee 100644 --- a/sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/ImageUploadViewModel.java +++ b/sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/ImageUploadViewModel.java @@ -56,6 +56,7 @@ public boolean canCancel() { private final MutableLiveData transferSpeedLiveData = new MutableLiveData<>(); private final SingleLiveEvent errorLiveData = new SingleLiveEvent<>(); private final SingleLiveEvent cancelledEvent = new SingleLiveEvent<>(); + private final SingleLiveEvent hashAlreadyFound = new SingleLiveEvent<>(); private long uploadStartTimestamp; private int initialBytes; @@ -97,7 +98,12 @@ public LiveData getCancelledEvent() { return cancelledEvent; } - public void upload(@NonNull final byte[] data, final int image) { + @NonNull + public LiveData getHashAlreadyFoundEvent() { + return hashAlreadyFound; + } + + public void upload(@NonNull final byte[] data, final int image, boolean force) { if (controller != null) { return; } @@ -117,7 +123,6 @@ public void upload(@NonNull final byte[] data, final int image) { } final byte[] hash = tmpHash; - requestHighConnectionPriority(); manager.list(new McuMgrCallback<>() { @Override public void onResponse(@NonNull final McuMgrImageStateResponse response) { @@ -130,16 +135,13 @@ public void onResponse(@NonNull final McuMgrImageStateResponse response) { } } // If yes, no need to send again. - if (theSameImage != null) { - if (theSameImage.slot == 0) { - errorLiveData.postValue(new McuMgrException("Firmware already active.")); - } else { - // Firmware is identical to one on slot 1. No need to send anything. - stateLiveData.postValue(State.COMPLETE); - } + if (!force && theSameImage != null) { + hashAlreadyFound.postValue(theSameImage.active); postReady(); return; } + + requestHighConnectionPriority(); // Otherwise, send the firmware. This may return NO MEMORY error if slot 1 is // filled with an image with pending or confirmed flags set. stateLiveData.postValue(State.UPLOADING); @@ -229,16 +231,14 @@ public void onUploadCompleted() { private void requestHighConnectionPriority() { final McuMgrTransport transporter = manager.getTransporter(); - if (transporter instanceof McuMgrBleTransport) { - final McuMgrBleTransport bleTransporter = (McuMgrBleTransport) transporter; + if (transporter instanceof McuMgrBleTransport bleTransporter) { bleTransporter.requestConnPriority(ConnectionPriorityRequest.CONNECTION_PRIORITY_HIGH); } } private void setLoggingEnabled(final boolean enabled) { final McuMgrTransport transporter = manager.getTransporter(); - if (transporter instanceof McuMgrBleTransport) { - final McuMgrBleTransport bleTransporter = (McuMgrBleTransport) transporter; + if (transporter instanceof McuMgrBleTransport bleTransporter) { bleTransporter.setLoggingEnabled(enabled); } } diff --git a/sample/src/main/res/values/strings_image_upload.xml b/sample/src/main/res/values/strings_image_upload.xml index 71a0c4e4..df0c1fee 100644 --- a/sample/src/main/res/values/strings_image_upload.xml +++ b/sample/src/main/res/values/strings_image_upload.xml @@ -23,6 +23,10 @@ Image 3 (3) + Overwrite? + The selected image is already on the device. Do you want to send it again? + The selected image is already active. Do you want to send it again? + READY VALIDATING… UPLOADING…