Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ public Void answer(InvocationOnMock invocation) {
taskmaster.start();
when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
softButtonManager = new SoftButtonManager(internalInterface, fileManager);
softButtonManager.isDynamicGraphicSupported = true;


// When internalInterface.sendRPC() is called inside SoftButtonManager:
Expand Down Expand Up @@ -516,4 +517,27 @@ public void testAssignSameNameStateListToSoftButtonObject() {

assertEquals(stateListUnique, softButtonObject.getStates());
}

@Test
public void testSoftButtonManagerGraphicNotSupported() {
softButtonManager.isDynamicGraphicSupported = false;
fileManagerUploadArtworksListenerCalledCounter = 0;
internalInterfaceSendRPCListenerCalledCounter = 0;

softButtonManager.setSoftButtonObjects(Arrays.asList(softButtonObject1, softButtonObject2));
assertEquals("SoftButtonManager is uploading artwork, when graphic is not supported", 0, fileManagerUploadArtworksListenerCalledCounter);
}

@Test
public void testSoftButtonManagerDynamicImageNotSupportedNoText() {
softButtonManager.isDynamicGraphicSupported = false;
fileManagerUploadArtworksListenerCalledCounter = 0;
internalInterfaceSendRPCListenerCalledCounter = 0;

SoftButtonState softButtonState = new SoftButtonState("testState", null, new SdlArtwork("image", FileType.GRAPHIC_PNG, 1, true));
SoftButtonObject softButtonObject = new SoftButtonObject("obj1", softButtonState, null);

softButtonManager.setSoftButtonObjects(Arrays.asList(softButtonObject));
assertEquals("SoftButtonManager is uploading artwork, when graphic is not supported", 0, fileManagerUploadArtworksListenerCalledCounter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
import com.smartdevicelink.proxy.rpc.DisplayCapability;
import com.smartdevicelink.proxy.rpc.OnButtonEvent;
import com.smartdevicelink.proxy.rpc.OnButtonPress;
Expand All @@ -58,7 +59,6 @@

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

Expand All @@ -71,6 +71,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
private static final String TAG = "BaseSoftButtonManager";
private final WeakReference<FileManager> fileManager;
SoftButtonCapabilities softButtonCapabilities;
boolean isDynamicGraphicSupported;
private CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
private HMILevel currentHMILevel;
private final OnSystemCapabilityListener onDisplayCapabilityListener;
Expand Down Expand Up @@ -99,6 +100,11 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
this.currentHMILevel = null;
this.transactionQueue = newTransactionQueue();
this.batchQueue = new ArrayList<>();
DisplayCapabilities displayCapabilities = null;
if (internalInterface.getSystemCapabilityManager() != null) {
displayCapabilities = (DisplayCapabilities) this.internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.DISPLAY, null, false);
}
isDynamicGraphicSupported = (displayCapabilities != null && displayCapabilities.getGraphicSupported() != null) ? displayCapabilities.getGraphicSupported() : true;

this.updateListener = new SoftButtonObject.UpdateListener() {
@Override
Expand Down Expand Up @@ -152,7 +158,7 @@ public void onCapabilityRetrieved(Object capability) {

// Auto-send an updated Show if we have new capabilities
if (softButtonObjects != null && !softButtonObjects.isEmpty() && softButtonCapabilities != null && !softButtonCapabilitiesEquals(oldSoftButtonCapabilities, softButtonCapabilities)) {
SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager, softButtonCapabilities, softButtonObjects, getCurrentMainField1());
SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager, softButtonCapabilities, softButtonObjects, getCurrentMainField1(), isDynamicGraphicSupported);
transactionQueue.add(operation, false);
}
}
Expand Down Expand Up @@ -311,7 +317,7 @@ protected void setSoftButtonObjects(@NonNull List<SoftButtonObject> list) {
this.softButtonObjects = softButtonObjects;

// We only need to pass the first softButtonCapabilities in the array due to the fact that all soft button capabilities are the same (i.e. there is no way to assign a softButtonCapabilities to a specific soft button).
SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager.get(), softButtonCapabilities, softButtonObjects, getCurrentMainField1());
SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager.get(), softButtonCapabilities, softButtonObjects, getCurrentMainField1(), isDynamicGraphicSupported);

if (batchUpdates) {
batchQueue.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.smartdevicelink.proxy.rpc.Show;
import com.smartdevicelink.proxy.rpc.SoftButton;
import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
import com.smartdevicelink.proxy.rpc.enums.ImageType;
import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.util.DebugTool;
Expand All @@ -30,14 +31,16 @@ class SoftButtonReplaceOperation extends Task {
private final SoftButtonCapabilities softButtonCapabilities;
private final CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
private String currentMainField1;
private Boolean isDynamicGraphicSupported;

SoftButtonReplaceOperation(ISdl internalInterface, FileManager fileManager, SoftButtonCapabilities softButtonCapabilities, CopyOnWriteArrayList<SoftButtonObject> softButtonObjects, String currentMainField1) {
SoftButtonReplaceOperation(ISdl internalInterface, FileManager fileManager, SoftButtonCapabilities softButtonCapabilities, CopyOnWriteArrayList<SoftButtonObject> softButtonObjects, String currentMainField1, Boolean isDynamicGraphicSupported) {
super("SoftButtonReplaceOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.fileManager = new WeakReference<>(fileManager);
this.softButtonCapabilities = softButtonCapabilities;
this.softButtonObjects = softButtonObjects;
this.currentMainField1 = currentMainField1;
this.isDynamicGraphicSupported = isDynamicGraphicSupported;
}

@Override
Expand Down Expand Up @@ -65,6 +68,18 @@ public void onComplete(boolean success) {
onFinished();
}
});
} else if (!supportsDynamicSoftButtonImages()) {
DebugTool.logInfo(TAG, "Soft button images are not supported. Attempting to send text and static image only soft buttons. If any button does not contain text and/or a static image, no buttons will be sent.");
sendCurrentStateStaticImageOnlySoftButtons(new CompletionListener() {
@Override
public void onComplete(boolean success) {
if (!success) {
DebugTool.logError(TAG, "Buttons will not be sent because the module does not support dynamic images and some of the buttons do not have text or static images");
}
onFinished();
}
});

} else if (currentStateHasImages() && !allCurrentStateImagesAreUploaded()) {
// If there are images that aren't uploaded
// Send text buttons if all the soft buttons have text
Expand Down Expand Up @@ -128,6 +143,13 @@ private void uploadInitialStateImages(final CompletionListener completionListene
}
return;
}
if (!supportsDynamicSoftButtonImages()) {
DebugTool.logInfo(TAG, "Head unit does not support dynamic images, skipping upload");
if (completionListener != null) {
completionListener.onComplete(false);
}
return;
}

DebugTool.logInfo(TAG, "Uploading soft button initial artworks");
if (fileManager.get() != null) {
Expand Down Expand Up @@ -236,6 +258,59 @@ public void onResponse(int correlationId, RPCResponse response) {
}
}

// Send soft buttons for the current state that only contain text and static images only, if possible.
private void sendCurrentStateStaticImageOnlySoftButtons(final CompletionListener completionListener) {
if (getState() == Task.CANCELED) {
onFinished();
}

DebugTool.logInfo(TAG, "Preparing to send text and static image only soft buttons");
List<SoftButton> textButtons = new ArrayList<>();
for (SoftButtonObject softButtonObject : softButtonObjects) {
SoftButton softButton = softButtonObject.getCurrentStateSoftButton();
if (softButton.getText() == null && softButton.getImage() != null && softButton.getImage().getImageType() == ImageType.DYNAMIC) {
DebugTool.logWarning(TAG, "Attempted to create text and static image only buttons, but some buttons don't support text and have dynamic images, so no soft buttons will be sent.");
if (completionListener != null) {
completionListener.onComplete(false);
}
return;
}


if (softButton.getImage() != null && softButton.getImage().getImageType() == ImageType.DYNAMIC) {
// We should create a new softButtonObject rather than modifying the original one
SoftButton textAndStaticImageOnlySoftButton = new SoftButton(SoftButtonType.SBT_TEXT, softButton.getSoftButtonID());
textAndStaticImageOnlySoftButton.setText(softButton.getText());
textAndStaticImageOnlySoftButton.setSystemAction(softButton.getSystemAction());
textAndStaticImageOnlySoftButton.setIsHighlighted(softButton.getIsHighlighted());
textAndStaticImageOnlySoftButton.setImage(softButton.getImage());
textButtons.add(textAndStaticImageOnlySoftButton);
} else {
textButtons.add(softButton);
}
}

Show show = new Show();
show.setOnRPCResponseListener(new OnRPCResponseListener() {
@Override
public void onResponse(int correlationId, RPCResponse response) {
if (response.getSuccess()) {
DebugTool.logInfo(TAG, "Finished sending text and static image only soft buttons");
} else {
DebugTool.logWarning(TAG, "Failed to update soft buttons with text and static image only buttons");
}
if (completionListener != null) {
completionListener.onComplete(response.getSuccess());
}
}
});
show.setMainField1(currentMainField1);
show.setSoftButtons(textButtons);
if (internalInterface.get() != null) {
internalInterface.get().sendRPC(show);
}
}


// Returns text soft buttons representing the current states of the button objects, or returns if _any_ of the buttons' current states are image only buttons.
private void sendCurrentStateTextOnlySoftButtons(final CompletionListener completionListener) {
Expand Down Expand Up @@ -303,6 +378,10 @@ private boolean allCurrentStateImagesAreUploaded() {
return true;
}

private boolean supportsDynamicSoftButtonImages() {
return softButtonCapabilities != null && Boolean.TRUE.equals(isDynamicGraphicSupported) && Boolean.TRUE.equals(softButtonCapabilities.getImageSupported());
}

private boolean supportsSoftButtonImages() {
return softButtonCapabilities != null && Boolean.TRUE.equals(softButtonCapabilities.getImageSupported());
}
Expand Down