diff options
Diffstat (limited to 'base/src/main/java/com/smartdevicelink/managers/screen')
9 files changed, 962 insertions, 701 deletions
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java index 0a679906c..e13855d1c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java @@ -79,6 +79,7 @@ abstract class BaseScreenManager extends BaseSubManager { // Sub manager listener private final CompletionListener subManagerListener = new CompletionListener() { + @Override public synchronized void onComplete(boolean success) { if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null && subscribeButtonManager != null) { @@ -251,6 +252,9 @@ abstract class BaseScreenManager extends BaseSubManager { * @return an SdlArtwork object represents the current primaryGraphic */ public SdlArtwork getPrimaryGraphic() { + if (this.textAndGraphicManager.getPrimaryGraphic() == null || textAndGraphicManager.getPrimaryGraphic().getName() == null || this.textAndGraphicManager.getPrimaryGraphic().getName().equals(textAndGraphicManager.getBlankArtwork().getName())) { + return null; + } return this.textAndGraphicManager.getPrimaryGraphic(); } @@ -270,6 +274,9 @@ abstract class BaseScreenManager extends BaseSubManager { * @return an SdlArtwork object represents the current secondaryGraphic */ public SdlArtwork getSecondaryGraphic() { + if (this.textAndGraphicManager.getSecondaryGraphic() == null || textAndGraphicManager.getSecondaryGraphic().getName() == null || this.textAndGraphicManager.getSecondaryGraphic().getName().equals(textAndGraphicManager.getBlankArtwork().getName())) { + return null; + } return this.textAndGraphicManager.getSecondaryGraphic(); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseTextAndGraphicManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseTextAndGraphicManager.java index ad43b5526..9c756e758 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseTextAndGraphicManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseTextAndGraphicManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Livio, Inc. + * Copyright (c) 2019 - 2020 Livio, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,61 +33,51 @@ package com.smartdevicelink.managers.screen; import androidx.annotation.NonNull; +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Task; import com.smartdevicelink.managers.BaseSubManager; import com.smartdevicelink.managers.CompletionListener; -import com.smartdevicelink.managers.ManagerUtility; import com.smartdevicelink.managers.file.FileManager; -import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener; import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager; import com.smartdevicelink.protocol.enums.FunctionID; import com.smartdevicelink.proxy.RPCNotification; -import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.interfaces.ISdl; import com.smartdevicelink.proxy.rpc.DisplayCapability; -import com.smartdevicelink.proxy.rpc.MetadataTags; import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.Show; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MetadataType; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.TextAlignment; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; -import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; -import com.smartdevicelink.util.CompareUtils; import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -import java.util.Map; import static com.smartdevicelink.proxy.rpc.enums.TextAlignment.CENTERED; /** * <strong>TextAndGraphicManager</strong> <br> - * + * <p> * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br> - * */ abstract class BaseTextAndGraphicManager extends BaseSubManager { private static final String TAG = "TextAndGraphicManager"; - boolean isDirty, hasQueuedUpdate; - volatile Show inProgressUpdate; - Show currentScreenData, queuedImageUpdate; + boolean isDirty; + Show currentScreenData; HMILevel currentHMILevel; + private final WeakReference<SoftButtonManager> softButtonManager; WindowCapability defaultMainWindowCapability; - private boolean pendingHMIFull, batchingUpdates; + private boolean batchingUpdates; private final WeakReference<FileManager> fileManager; - private final WeakReference<SoftButtonManager> softButtonManager; - private CompletionListener queuedUpdateListener, inProgressListener, pendingHMIListener; SdlArtwork blankArtwork; private OnRPCNotificationListener hmiListener; private OnSystemCapabilityListener onDisplaysCapabilityListener; @@ -95,6 +85,9 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { private TextAlignment textAlignment; private String textField1, textField2, textField3, textField4, mediaTrackTextField, title; private MetadataType textField1Type, textField2Type, textField3Type, textField4Type; + TextAndGraphicUpdateOperation updateOperation; + private CompletionListener currentOperationListener; + Queue transactionQueue; //Constructors @@ -105,12 +98,11 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { this.softButtonManager = new WeakReference<>(softButtonManager); batchingUpdates = false; isDirty = false; - pendingHMIFull = false; textAlignment = CENTERED; currentHMILevel = HMILevel.HMI_NONE; currentScreenData = new Show(); + this.transactionQueue = newTransactionQueue(); addListeners(); - getBlankArtwork(); } @Override @@ -120,8 +112,7 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { } @Override - public void dispose(){ - + public void dispose() { textField1 = null; textField1Type = null; textField2 = null; @@ -137,15 +128,15 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { secondaryGraphic = null; blankArtwork = null; defaultMainWindowCapability = null; - inProgressUpdate = null; - queuedImageUpdate = null; currentScreenData = null; - queuedUpdateListener = null; - pendingHMIListener = null; - inProgressListener = null; - hasQueuedUpdate = false; isDirty = false; - pendingHMIFull = false; + updateOperation = null; + + // Cancel the operations + if (transactionQueue != null) { + transactionQueue.close(); + transactionQueue = null; + } // remove listeners internalInterface.removeOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener); @@ -154,66 +145,32 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { super.dispose(); } - private void addListeners() { - // add listener - hmiListener = new OnRPCNotificationListener() { - @Override - public void onNotified(RPCNotification notification) { - OnHMIStatus onHMIStatus = (OnHMIStatus)notification; - if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { - return; - } - currentHMILevel = onHMIStatus.getHmiLevel(); - if (currentHMILevel == HMILevel.HMI_FULL){ - if (pendingHMIFull){ - DebugTool.logInfo(TAG, "Acquired HMI_FULL with pending update. Sending now"); - pendingHMIFull = false; - sdlUpdate(pendingHMIListener); - pendingHMIListener = null; - } - } - } - }; - internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener); - - - onDisplaysCapabilityListener = new OnSystemCapabilityListener() { - @Override - public void onCapabilityRetrieved(Object capability) { - // instead of using the parameter it's more safe to use the convenience method - List<DisplayCapability> capabilities = SystemCapabilityManager.convertToList(capability, DisplayCapability.class); - if (capabilities == null || capabilities.size() == 0) { - DebugTool.logError(TAG, "TextAndGraphic Manager - Capabilities sent here are null or empty"); - }else { - DisplayCapability display = capabilities.get(0); - for (WindowCapability windowCapability : display.getWindowCapabilities()) { - int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue(); - if (currentWindowID == PredefinedWindows.DEFAULT_WINDOW.getValue()) { - defaultMainWindowCapability = windowCapability; - } - } - } - } + private Queue newTransactionQueue() { + Queue queue = internalInterface.getTaskmaster().createQueue("TextAndGraphicManager", 3, false); + queue.pause(); + return queue; + } - @Override - public void onError(String info) { - DebugTool.logError(TAG, "Display Capability cannot be retrieved"); - defaultMainWindowCapability = null; - } - }; - this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener); + // Suspend the queue if the WindowCapabilities are null + // OR if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE + private void updateTransactionQueueSuspended() { + if (defaultMainWindowCapability == null || HMILevel.HMI_NONE.equals(currentHMILevel)) { + DebugTool.logInfo(TAG, String.format("Suspending the transaction queue. Current HMI level is NONE: %b, window capabilities are null: %b", HMILevel.HMI_NONE.equals(currentHMILevel), defaultMainWindowCapability == null)); + transactionQueue.pause(); + } else { + DebugTool.logInfo(TAG, "Starting the transaction queue"); + transactionQueue.resume(); + } } // Upload / Send protected void update(CompletionListener listener) { - // check if is batch update if (batchingUpdates) { return; } - - if (isDirty){ + if (isDirty) { isDirty = false; sdlUpdate(listener); } else if (listener != null) { @@ -221,754 +178,330 @@ abstract class BaseTextAndGraphicManager extends BaseSubManager { } } - private synchronized void sdlUpdate(CompletionListener listener){ - - // make sure hmi is not none - if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE){ - //Trying to send show on HMI_NONE, waiting for full - pendingHMIFull = true; - if (listener != null){ - pendingHMIListener = listener; - } - return; - } - - //Updating Text and Graphics - if (inProgressUpdate != null){ - - //In progress update exists, queueing update - if (queuedUpdateListener != null){ - - //Queued update already exists, superseding previous queued update - queuedUpdateListener.onComplete(false); - queuedUpdateListener = null; - } - - if (listener != null){ - queuedUpdateListener = listener; - } - - hasQueuedUpdate = true; - - return; - } - - Show fullShow = new Show(); - fullShow.setAlignment(textAlignment); - fullShow = assembleShowText(fullShow); - fullShow = assembleShowImages(fullShow); - - inProgressListener = listener; - - if (!shouldUpdatePrimaryImage() && !shouldUpdateSecondaryImage()){ - - //No Images to send, only sending text - inProgressUpdate = extractTextFromShow(fullShow); - sendShow(); - - }else if (!sdlArtworkNeedsUpload(primaryGraphic) && (secondaryGraphic == blankArtwork || !sdlArtworkNeedsUpload(secondaryGraphic))){ - - //Images already uploaded, sending full update - // The files to be updated are already uploaded, send the full show immediately - inProgressUpdate = fullShow; - sendShow(); - } else{ - - // Images need to be uploaded, sending text and uploading images - inProgressUpdate = fullShow; - final Show thisUpdate = fullShow; - - uploadImages(new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success){ - DebugTool.logError(TAG, "Error uploading image"); - inProgressUpdate = extractTextFromShow(inProgressUpdate); - sendShow(); - } - // Check if queued image update still matches our images (there could have been a new Show in the meantime) - // and send a new update if it does. Since the images will already be on the head unit, the whole show will be sent - if (thisUpdate.getGraphic() != null && thisUpdate.getGraphic().equals(queuedImageUpdate.getGraphic()) || - (thisUpdate.getSecondaryGraphic() != null && queuedImageUpdate.getSecondaryGraphic() != null) && thisUpdate.getSecondaryGraphic().equals(queuedImageUpdate.getSecondaryGraphic())){ - // Queued image update matches the images we need, sending update - sendShow(); - } - // Else, Queued image update does not match the images we need, skipping update - } - }); - queuedImageUpdate = fullShow; - } - } - - private void sendShow(){ - inProgressUpdate.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - handleResponse(response.getSuccess()); - } - - private void handleResponse(boolean success){ - if (success){ - updateCurrentScreenDataState(inProgressUpdate); - } - - inProgressUpdate = null; - if (inProgressListener != null){ - inProgressListener.onComplete(success); - inProgressListener = null; - } - - if (hasQueuedUpdate){ - //Queued update exists, sending another update - hasQueuedUpdate = false; - CompletionListener temp = queuedUpdateListener; - queuedUpdateListener = null; - sdlUpdate(temp); - } - } - }); - - if (this.softButtonManager.get() != null) { - this.softButtonManager.get().setCurrentMainField1(inProgressUpdate.getMainField1()); - } - internalInterface.sendRPC(inProgressUpdate); - } - - // Images - - private void uploadImages(final CompletionListener listener) { - - List<SdlArtwork> artworksToUpload = new ArrayList<>(); - - // add primary image - if (shouldUpdatePrimaryImage() && !primaryGraphic.isStaticIcon()){ - artworksToUpload.add(primaryGraphic); - } - - // add secondary image - if (shouldUpdateSecondaryImage() && !secondaryGraphic.isStaticIcon()){ - artworksToUpload.add(secondaryGraphic); - } - - if (artworksToUpload.size() == 0 && (primaryGraphic.isStaticIcon() || secondaryGraphic.isStaticIcon())){ - DebugTool.logInfo(TAG, "Upload attempted on static icons, sending them without upload instead"); - listener.onComplete(true); - } - - // use file manager to upload art - if (fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map<String, String> errors) { - if (errors != null) { - DebugTool.logError(TAG, "Error Uploading Artworks. Error: " + errors.toString()); - listener.onComplete(false); - } else { - listener.onComplete(true); - } - } - }); - } - } - - private Show assembleShowImages(Show show){ - - if (shouldUpdatePrimaryImage()){ - show.setGraphic(primaryGraphic.getImageRPC()); - } - - if (shouldUpdateSecondaryImage()){ - show.setSecondaryGraphic(secondaryGraphic.getImageRPC()); - } - - return show; - } - - // Text - - Show assembleShowText(Show show){ - - show = setBlankTextFields(show); - - if (mediaTrackTextField != null && shouldUpdateMediaTrackField()) { - show.setMediaTrack(mediaTrackTextField); - } - - if (title != null && shouldUpdateTitleField()) { - show.setTemplateTitle(title); - } - - List<String> nonNullFields = findValidMainTextFields(); - if (nonNullFields.isEmpty()){ - return show; - } - - int numberOfLines = defaultMainWindowCapability != null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfMainFieldLines(defaultMainWindowCapability) : 4; - - switch (numberOfLines) { - case 1: show = assembleOneLineShowText(show, nonNullFields); - break; - case 2: show = assembleTwoLineShowText(show); - break; - case 3: show = assembleThreeLineShowText(show); - break; - case 4: show = assembleFourLineShowText(show); - break; - } - - return show; - } - - private Show assembleOneLineShowText(Show show, List<String> showFields){ - - StringBuilder showString1 = new StringBuilder(); - for (int i = 0; i < showFields.size(); i++) { - if (i > 0) { - showString1.append(" - ").append(showFields.get(i)); - }else{ - showString1.append(showFields.get(i)); - } - } - show.setMainField1(showString1.toString()); - - MetadataTags tags = new MetadataTags(); - tags.setMainField1(findNonNullMetadataFields()); - - show.setMetadataTags(tags); - - return show; - } - - private Show assembleTwoLineShowText(Show show){ - - StringBuilder tempString = new StringBuilder(); - MetadataTags tags = new MetadataTags(); - - if (textField1 != null && textField1.length() > 0) { - tempString.append(textField1); - if (textField1Type != null){ - tags.setMainField1(textField1Type); + private synchronized void sdlUpdate(final CompletionListener listener) { + if (this.transactionQueue.getTasksAsList().size() > 0) { + // Transactions already in queue, we need to clear it out + transactionQueue.clear(); + updateOperation = null; + if (currentOperationListener != null) { + currentOperationListener.onComplete(false); } } - if (textField2 != null && textField2.length() > 0) { - if (( textField3 == null || !(textField3.length() > 0)) && (textField4 == null || !(textField4.length() > 0))){ - // text does not exist in slots 3 or 4, put text2 in slot 2 - show.setMainField2(textField2); - if (textField2Type != null){ - tags.setMainField2(textField2Type); - } - } else if (textField1 != null && textField1.length() > 0) { - // If text 1 exists, put it in slot 1 formatted - tempString.append(" - ").append(textField2); - if (textField2Type != null){ - List<MetadataType> typeList = new ArrayList<>(); - typeList.add(textField2Type); - if (textField1Type != null){ - typeList.add(textField1Type); - } - tags.setMainField1(typeList); - } - }else { - // If text 1 does not exist, put it in slot 1 unformatted - tempString.append(textField2); - if (textField2Type != null){ - tags.setMainField1(textField2Type); - } + // Task can be READY, about to start and popped of the queue, so we have to cancel it, to prevent it from starting + if (updateOperation != null && updateOperation.getState() == Task.READY) { + updateOperation.cancelTask(); + if (currentOperationListener != null) { + currentOperationListener.onComplete(false); } } - // set mainfield 1 - show.setMainField1(tempString.toString()); - - // new stringbuilder object - tempString = new StringBuilder(); - - if (textField3 != null && textField3.length() > 0){ - // If text 3 exists, put it in slot 2 - tempString.append(textField3); - if (textField3Type != null){ - List<MetadataType> typeList = new ArrayList<>(); - typeList.add(textField3Type); - tags.setMainField2(typeList); - } + // If Task is IN_PROGRESS, it’s not on the queue, we need to mark it as cancelled. The task will return at some point when it checks its status and call the listener back + if (updateOperation != null && updateOperation.getState() == Task.IN_PROGRESS) { + updateOperation.cancelTask(); } - if (textField4 != null && textField4.length() > 0){ - if (textField3 != null && textField3.length() > 0){ - // If text 3 exists, put it in slot 2 formatted - tempString.append(" - ").append(textField4); - if (textField4Type != null){ - List<MetadataType> typeList = new ArrayList<>(); - typeList.add(textField4Type); - if (textField3Type != null){ - typeList.add(textField3Type); - } - tags.setMainField2(typeList); - } - } else { - // If text 3 does not exist, put it in slot 3 unformatted - tempString.append(textField4); - if (textField4Type != null){ - tags.setMainField2(textField4Type); - } - } - } + currentOperationListener = listener; - if (tempString.toString().length() > 0){ - show.setMainField2(tempString.toString()); - } - - show.setMetadataTags(tags); - return show; - } - - private Show assembleThreeLineShowText(Show show){ - - MetadataTags tags = new MetadataTags(); - - if (textField1 != null && textField1.length() > 0) { - show.setMainField1(textField1); - if (textField1Type != null){ - tags.setMainField1(textField1Type); - } - } - - if (textField2 != null && textField2.length() > 0) { - show.setMainField2(textField2); - if (textField2Type != null){ - tags.setMainField2(textField2Type); - } - } - - StringBuilder tempString = new StringBuilder(); - - if (textField3 != null && textField3.length() > 0){ - tempString.append(textField3); - if (textField3Type != null){ - tags.setMainField3(textField3Type); - } - } - - if (textField4 != null && textField4.length() > 0) { - if (textField3 != null && textField3.length() > 0) { - // If text 3 exists, put it in slot 3 formatted - tempString.append(" - ").append(textField4); - if (textField4Type != null){ - List<MetadataType> tags4 = new ArrayList<>(); - if (textField3Type != null){ - tags4.add(textField3Type); - } - tags4.add(textField4Type); - tags.setMainField3(tags4); - } - } else { - // If text 3 does not exist, put it in slot 3 formatted - tempString.append(textField4); - if (textField4Type != null){ - tags.setMainField3(textField4Type); - } + CurrentScreenDataUpdatedListener currentScreenDataUpdateListener = new CurrentScreenDataUpdatedListener() { + @Override + public void onUpdate(Show show) { + updatePendingOperationsWithNewScreenData(show); + currentScreenData = show; } - } + }; - show.setMainField3(tempString.toString()); - show.setMetadataTags(tags); - return show; + updateOperation = new TextAndGraphicUpdateOperation(internalInterface, fileManager.get(), defaultMainWindowCapability, currentScreenData, currentState(), currentOperationListener, currentScreenDataUpdateListener); + transactionQueue.add(updateOperation, false); } - private Show assembleFourLineShowText(Show show){ - - MetadataTags tags = new MetadataTags(); - - if (textField1 != null && textField1.length() > 0) { - show.setMainField1(textField1); - if (textField1Type != null){ - tags.setMainField1(textField1Type); + //Updates pending task with current screen data + void updatePendingOperationsWithNewScreenData(Show newScreenData) { + for (Task task : transactionQueue.getTasksAsList()) { + if (!(task instanceof TextAndGraphicUpdateOperation)) { + continue; } + ((TextAndGraphicUpdateOperation) task).setCurrentScreenData(newScreenData); } - - if (textField2 != null && textField2.length() > 0) { - show.setMainField2(textField2); - if (textField2Type != null){ - tags.setMainField2(textField2Type); - } + if (this.softButtonManager.get() != null && newScreenData.getMainField1() != null) { + this.softButtonManager.get().setCurrentMainField1(currentScreenData.getMainField1()); } - - if (textField3 != null && textField3.length() > 0) { - show.setMainField3(textField3); - if (textField3Type != null){ - tags.setMainField3(textField3Type); - } - } - - if (textField4 != null && textField4.length() > 0) { - show.setMainField4(textField4); - if (textField4Type != null){ - tags.setMainField4(textField4Type); - } - } - - show.setMetadataTags(tags); - return show; } - // Extraction - - Show extractTextFromShow(Show show){ - - Show newShow = new Show(); - newShow.setMainField1(show.getMainField1()); - newShow.setMainField2(show.getMainField2()); - newShow.setMainField3(show.getMainField3()); - newShow.setMainField4(show.getMainField4()); - newShow.setTemplateTitle(show.getTemplateTitle()); - - return newShow; - } - - private Show setBlankTextFields(Show newShow){ - - newShow.setMainField1(""); - newShow.setMainField2(""); - newShow.setMainField3(""); - newShow.setMainField4(""); - newShow.setMediaTrack(""); - newShow.setTemplateTitle(""); - - return newShow; - } - - private void updateCurrentScreenDataState(Show show){ - - if (show == null){ - DebugTool.logError(TAG, "can not updateCurrentScreenDataFromShow from null show"); - return; - } - - // If the items are null, they were not updated, so we can't just set it directly - if (show.getMainField1() != null){ - currentScreenData.setMainField1(show.getMainField1()); - } - if (show.getMainField2() != null){ - currentScreenData.setMainField2(show.getMainField2()); - } - if (show.getMainField3() != null){ - currentScreenData.setMainField3(show.getMainField3()); - } - if (show.getMainField4() != null){ - currentScreenData.setMainField4(show.getMainField4()); - } - if (show.getTemplateTitle() != null){ - currentScreenData.setTemplateTitle(show.getTemplateTitle()); - } - if (show.getMediaTrack() != null){ - currentScreenData.setMediaTrack(show.getMediaTrack()); - } - if (show.getMetadataTags() != null){ - currentScreenData.setMetadataTags(show.getMetadataTags()); - } - if (show.getAlignment() != null){ - currentScreenData.setAlignment(show.getAlignment()); - } - if (show.getGraphic() != null){ - currentScreenData.setGraphic(show.getGraphic()); - } - if (show.getSecondaryGraphic() != null){ - currentScreenData.setSecondaryGraphic(show.getSecondaryGraphic()); - } + interface CurrentScreenDataUpdatedListener { + void onUpdate(Show show); } - // Helpers - private List<String> findValidMainTextFields(){ + private List<String> findNonNullTextFields() { List<String> array = new ArrayList<>(); - if (textField1 != null && textField1.length() > 0) { + if (textField1 != null) { array.add(textField1); } - if (textField2 != null && textField2.length() > 0) { + if (textField2 != null) { array.add(textField2); } - if (textField3 != null && textField3.length() > 0) { + if (textField3 != null) { array.add(textField3); } - if (textField4 != null && textField4.length() > 0) { + if (textField4 != null) { array.add(textField4); } - return array; - } - - - private List<MetadataType> findNonNullMetadataFields(){ - List<MetadataType> array = new ArrayList<>(); - - if (textField1Type != null) { - array.add(textField1Type); - } - - if (textField2Type != null) { - array.add(textField2Type); + if (title != null) { + array.add(title); } - if (textField3Type != null) { - array.add(textField3Type); - } - - if (textField4Type != null) { - array.add(textField4Type); + if (mediaTrackTextField != null) { + array.add(mediaTrackTextField); } return array; } - abstract SdlArtwork getBlankArtwork(); + Boolean hasData() { + boolean hasTextFields = (findNonNullTextFields().size() > 0); + boolean hasImageFields = (primaryGraphic != null) || (secondaryGraphic != null); - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean sdlArtworkNeedsUpload(SdlArtwork artwork){ - if (fileManager.get() != null) { - return artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon(); - } - return false; + return hasTextFields || hasImageFields; } - /** - * Check to see if primaryGraphic should be updated - * @return true if primaryGraphic should be updated, false if not - */ - private boolean shouldUpdatePrimaryImage() { - boolean templateSupportsPrimaryArtwork = templateSupportsImageField(ImageFieldName.graphic); - - String currentScreenDataPrimaryGraphicName = (currentScreenData != null && currentScreenData.getGraphic() != null) ? currentScreenData.getGraphic().getValue() : null; - String primaryGraphicName = primaryGraphic != null ? primaryGraphic.getName() : null; - return templateSupportsPrimaryArtwork - && !CompareUtils.areStringsEqual(currentScreenDataPrimaryGraphicName, primaryGraphicName, true, true) - && primaryGraphic != null; - } - - /** - * Check to see if secondaryGraphic should be updated - * @return true if secondaryGraphic should be updated, false if not - */ - private boolean shouldUpdateSecondaryImage() { - boolean templateSupportsSecondaryArtwork = (templateSupportsImageField(ImageFieldName.graphic) || templateSupportsImageField(ImageFieldName.secondaryGraphic)); - - String currentScreenDataSecondaryGraphicName = (currentScreenData != null && currentScreenData.getSecondaryGraphic() != null) ? currentScreenData.getSecondaryGraphic().getValue() : null; - String secondaryGraphicName = secondaryGraphic != null ? secondaryGraphic.getName() : null; - return templateSupportsSecondaryArtwork - && !CompareUtils.areStringsEqual(currentScreenDataSecondaryGraphicName, secondaryGraphicName, true, true) - && secondaryGraphic != null; - } - - /** - * Check to see if template supports the specified image field - * @return true if image field is supported, false if not - */ - private boolean templateSupportsImageField(ImageFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); - } + abstract SdlArtwork getBlankArtwork(); - /** - * Check to see if mediaTrackTextField should be updated - * @return true if mediaTrackTextField should be updated, false if not - */ - private boolean shouldUpdateMediaTrackField() { - return templateSupportsTextField(TextFieldName.mediaTrack); - } - /** - * Check to see if title should be updated - * @return true if title should be updated, false if not - */ - private boolean shouldUpdateTitleField() { - return templateSupportsTextField(TextFieldName.templateTitle); - } + // Convert to State - /** - * Check to see if field should be updated - * @return true if field should be updated, false if not - */ - private boolean templateSupportsTextField(TextFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); + private TextsAndGraphicsState currentState() { + return new TextsAndGraphicsState(textField1, textField2, textField3, textField4, mediaTrackTextField, + title, primaryGraphic, secondaryGraphic, textAlignment, textField1Type, textField2Type, textField3Type, textField4Type); } - // SCREEN ITEM SETTERS AND GETTERS + // Getters / Setters - void setTextAlignment(TextAlignment textAlignment){ + void setTextAlignment(TextAlignment textAlignment) { this.textAlignment = textAlignment; // If we aren't batching, send the update immediately, if we are, set ourselves as dirty (so we know we should send an update after the batch ends) - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - TextAlignment getTextAlignment(){ + TextAlignment getTextAlignment() { return textAlignment; } - void setMediaTrackTextField(String mediaTrackTextField){ + void setMediaTrackTextField(String mediaTrackTextField) { this.mediaTrackTextField = mediaTrackTextField; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getMediaTrackTextField(){ + String getMediaTrackTextField() { return mediaTrackTextField; } - void setTextField1(String textField1){ + void setTextField1(String textField1) { this.textField1 = textField1; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getTextField1(){ + String getTextField1() { return textField1; } - void setTextField2(String textField2){ + void setTextField2(String textField2) { this.textField2 = textField2; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getTextField2(){ + String getTextField2() { return textField2; } - void setTextField3(String textField3){ + void setTextField3(String textField3) { this.textField3 = textField3; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getTextField3(){ + String getTextField3() { return textField3; } - void setTextField4(String textField4){ + void setTextField4(String textField4) { this.textField4 = textField4; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getTextField4(){ + String getTextField4() { return textField4; } - void setTextField1Type(MetadataType textField1Type){ + void setTextField1Type(MetadataType textField1Type) { this.textField1Type = textField1Type; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - MetadataType getTextField1Type(){ + MetadataType getTextField1Type() { return textField1Type; } - void setTextField2Type(MetadataType textField2Type){ + void setTextField2Type(MetadataType textField2Type) { this.textField2Type = textField2Type; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - MetadataType getTextField2Type(){ + MetadataType getTextField2Type() { return textField2Type; } - void setTextField3Type(MetadataType textField3Type){ + void setTextField3Type(MetadataType textField3Type) { this.textField3Type = textField3Type; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - MetadataType getTextField3Type(){ + MetadataType getTextField3Type() { return textField3Type; } - void setTextField4Type(MetadataType textField4Type){ + void setTextField4Type(MetadataType textField4Type) { this.textField4Type = textField4Type; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - MetadataType getTextField4Type(){ + MetadataType getTextField4Type() { return textField4Type; } - void setTitle(String title){ + void setTitle(String title) { this.title = title; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - String getTitle(){ + String getTitle() { return title; } - void setPrimaryGraphic(SdlArtwork primaryGraphic){ + void setPrimaryGraphic(SdlArtwork primaryGraphic) { this.primaryGraphic = primaryGraphic; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - SdlArtwork getPrimaryGraphic(){ + SdlArtwork getPrimaryGraphic() { return primaryGraphic; } - void setSecondaryGraphic(SdlArtwork secondaryGraphic){ + void setSecondaryGraphic(SdlArtwork secondaryGraphic) { this.secondaryGraphic = secondaryGraphic; - if (!batchingUpdates){ + if (!batchingUpdates) { sdlUpdate(null); - }else{ + } else { isDirty = true; } } - SdlArtwork getSecondaryGraphic(){ + SdlArtwork getSecondaryGraphic() { return secondaryGraphic; } - void setBatchUpdates(boolean batching){ + void setBatchUpdates(boolean batching) { this.batchingUpdates = batching; } + private void addListeners() { + // add listener + hmiListener = new OnRPCNotificationListener() { + @Override + public void onNotified(RPCNotification notification) { + OnHMIStatus onHMIStatus = (OnHMIStatus) notification; + if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { + return; + } + currentHMILevel = onHMIStatus.getHmiLevel(); + updateTransactionQueueSuspended(); + } + }; + internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener); + + onDisplaysCapabilityListener = new OnSystemCapabilityListener() { + @Override + public void onCapabilityRetrieved(Object capability) { + // instead of using the parameter it's more safe to use the convenience method + List<DisplayCapability> capabilities = SystemCapabilityManager.convertToList(capability, DisplayCapability.class); + if (capabilities == null || capabilities.size() == 0) { + DebugTool.logError(TAG, "TextAndGraphic Manager - Capabilities sent here are null or empty"); + defaultMainWindowCapability = null; + } else { + DisplayCapability display = capabilities.get(0); + for (WindowCapability windowCapability : display.getWindowCapabilities()) { + int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue(); + if (currentWindowID == PredefinedWindows.DEFAULT_WINDOW.getValue()) { + defaultMainWindowCapability = windowCapability; + } + } + } + // Update the queue's suspend state + updateTransactionQueueSuspended(); + if (hasData()) { + sdlUpdate(null); + } + } + + @Override + public void onError(String info) { + DebugTool.logError(TAG, "Display Capability cannot be retrieved"); + defaultMainWindowCapability = null; + updateTransactionQueueSuspended(); + } + }; + this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener); + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java new file mode 100644 index 000000000..5f29e0cf9 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java @@ -0,0 +1,638 @@ +package com.smartdevicelink.managers.screen; + +import com.livio.taskmaster.Task; +import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.managers.ManagerUtility; +import com.smartdevicelink.managers.file.FileManager; +import com.smartdevicelink.managers.file.MultipleFileCompletionListener; +import com.smartdevicelink.managers.file.filetypes.SdlArtwork; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.interfaces.ISdl; +import com.smartdevicelink.proxy.rpc.MetadataTags; +import com.smartdevicelink.proxy.rpc.Show; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.enums.MetadataType; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; +import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; +import com.smartdevicelink.util.CompareUtils; +import com.smartdevicelink.util.DebugTool; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by Julian Kast on 8/23/20. + */ +class TextAndGraphicUpdateOperation extends Task { + + private static final String TAG = "TextAndGraphicUpdateOperation"; + private final WeakReference<ISdl> internalInterface; + private final WeakReference<FileManager> fileManager; + WindowCapability defaultMainWindowCapability; + private Show currentScreenData; + private TextsAndGraphicsState updatedState; + private CompletionListener listener; + private TextAndGraphicManager.CurrentScreenDataUpdatedListener currentScreenDataUpdateListener; + + TextAndGraphicUpdateOperation(ISdl internalInterface, FileManager fileManager, WindowCapability currentCapabilities, + Show currentScreenData, TextsAndGraphicsState newState, CompletionListener listener, TextAndGraphicManager.CurrentScreenDataUpdatedListener currentScreenDataUpdateListener) { + super("TextAndGraphicUpdateOperation"); + this.internalInterface = new WeakReference<>(internalInterface); + this.fileManager = new WeakReference<>(fileManager); + this.defaultMainWindowCapability = currentCapabilities; + this.currentScreenData = currentScreenData; + this.updatedState = newState; + this.listener = listener; + this.currentScreenDataUpdateListener = currentScreenDataUpdateListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + finishOperation(false); + return; + } + + // Build a show with everything from `self.newState`, we'll pull things out later if we can. + Show fullShow = new Show(); + fullShow.setAlignment(updatedState.getTextAlignment()); + fullShow = assembleShowText(fullShow); + fullShow = assembleShowImages(fullShow); + + if (!shouldUpdatePrimaryImage() && !shouldUpdateSecondaryImage()) { + DebugTool.logInfo(TAG, "No images to send, sending text"); + // If there are no images to update, just send the text + sendShow(extractTextFromShow(fullShow), new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + + } else if (!sdlArtworkNeedsUpload(updatedState.getPrimaryGraphic()) && !sdlArtworkNeedsUpload(updatedState.getSecondaryGraphic())) { + DebugTool.logInfo(TAG, "Images already uploaded, sending full update"); + // The files to be updated are already uploaded, send the full show immediately + sendShow(fullShow, new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } else { + DebugTool.logInfo(TAG, "Images need to be uploaded, sending text and uploading images"); + + sendShow(extractTextFromShow(fullShow), new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + finishOperation(false); + return; + } + uploadImagesAndSendWhenDone(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + + } + }); + } + } + + private void sendShow(final Show show, final CompletionListener listener) { + show.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + DebugTool.logInfo(TAG, "Text and Graphic update completed"); + if (response.getSuccess()) { + updateCurrentScreenDataFromShow(show); + } + listener.onComplete(response.getSuccess()); + + } + }); + internalInterface.get().sendRPC(show); + } + + + private void uploadImagesAndSendWhenDone(final CompletionListener listener) { + uploadImages(new CompletionListener() { + @Override + public void onComplete(boolean success) { + Show showWithGraphics = createImageOnlyShowWithPrimaryArtwork(updatedState.getPrimaryGraphic(), updatedState.getSecondaryGraphic()); + if (showWithGraphics != null) { + DebugTool.logInfo(TAG, "Sending update with the successfully uploaded images"); + sendShow(showWithGraphics, new CompletionListener() { + @Override + public void onComplete(boolean success) { + listener.onComplete(success); + } + }); + } else { + DebugTool.logWarning(TAG, "All images failed to upload. No graphics to show, skipping update."); + listener.onComplete(false); + } + } + }); + } + + private void uploadImages(final CompletionListener listener) { + List<SdlArtwork> artworksToUpload = new ArrayList<>(); + + // add primary image + if (shouldUpdatePrimaryImage() && !updatedState.getPrimaryGraphic().isStaticIcon()) { + artworksToUpload.add(updatedState.getPrimaryGraphic()); + } + + // add secondary image + if (shouldUpdateSecondaryImage() && !updatedState.getSecondaryGraphic().isStaticIcon()) { + artworksToUpload.add(updatedState.getSecondaryGraphic()); + } + + if (artworksToUpload.size() == 0) { + DebugTool.logInfo(TAG, "No artworks need an upload, sending them without upload instead"); + listener.onComplete(true); + } + + // use file manager to upload art + if (fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map<String, String> errors) { + if (getState() == Task.CANCELED) { + finishOperation(false); + return; + } + if (errors != null) { + DebugTool.logError(TAG, "Text and graphic manager artwork failed to upload with error: " + errors.toString()); + listener.onComplete(false); + } else { + listener.onComplete(true); + } + } + }); + } + } + + private Show assembleShowImages(Show show) { + if (shouldUpdatePrimaryImage()) { + show.setGraphic(updatedState.getPrimaryGraphic().getImageRPC()); + } + + if (shouldUpdateSecondaryImage()) { + show.setSecondaryGraphic(updatedState.getSecondaryGraphic().getImageRPC()); + } + + return show; + } + + Show createImageOnlyShowWithPrimaryArtwork(SdlArtwork primaryArtwork, SdlArtwork secondaryArtwork) { + Show newShow = new Show(); + newShow.setGraphic((primaryArtwork != null && !(sdlArtworkNeedsUpload(primaryArtwork))) ? primaryArtwork.getImageRPC() : null); + newShow.setSecondaryGraphic((secondaryArtwork != null && !(sdlArtworkNeedsUpload(secondaryArtwork))) ? secondaryArtwork.getImageRPC() : null); + if (newShow.getGraphic() == null && newShow.getSecondaryGraphic() == null) { + DebugTool.logInfo(TAG, "No graphics to upload"); + return null; + } + return newShow; + } + + Show assembleShowText(Show show) { + show = setBlankTextFields(show); + + if (updatedState.getMediaTrackTextField() != null && shouldUpdateMediaTrackField()) { + show.setMediaTrack(updatedState.getMediaTrackTextField()); + } + + if (updatedState.getTitle() != null && shouldUpdateTitleField()) { + show.setTemplateTitle(updatedState.getTitle()); + } + + List<String> nonNullFields = findValidMainTextFields(); + if (nonNullFields.isEmpty()) { + return show; + } + + int numberOfLines = defaultMainWindowCapability != null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfMainFieldLines(defaultMainWindowCapability) : 4; + + switch (numberOfLines) { + case 1: + show = assembleOneLineShowText(show, nonNullFields); + break; + case 2: + show = assembleTwoLineShowText(show); + break; + case 3: + show = assembleThreeLineShowText(show); + break; + case 4: + show = assembleFourLineShowText(show); + break; + } + return show; + } + + private Show assembleOneLineShowText(Show show, List<String> showFields) { + StringBuilder showString1 = new StringBuilder(); + for (int i = 0; i < showFields.size(); i++) { + if (i > 0) { + showString1.append(" - ").append(showFields.get(i)); + } else { + showString1.append(showFields.get(i)); + } + } + show.setMainField1(showString1.toString()); + MetadataTags tags = new MetadataTags(); + tags.setMainField1(findNonNullMetadataFields()); + show.setMetadataTags(tags); + + return show; + } + + private Show assembleTwoLineShowText(Show show) { + StringBuilder tempString = new StringBuilder(); + MetadataTags tags = new MetadataTags(); + + if (updatedState.getTextField1() != null && updatedState.getTextField1().length() > 0) { + tempString.append(updatedState.getTextField1()); + if (updatedState.getTextField1Type() != null) { + tags.setMainField1(updatedState.getTextField1Type()); + } + } + + if (updatedState.getTextField2() != null && updatedState.getTextField2().length() > 0) { + if ((updatedState.getTextField3() == null || !(updatedState.getTextField3().length() > 0)) && (updatedState.getTextField4() == null || !(updatedState.getTextField4().length() > 0))) { + // text does not exist in slots 3 or 4, put text2 in slot 2 + show.setMainField2(updatedState.getTextField2()); + if (updatedState.getTextField2Type() != null) { + tags.setMainField2(updatedState.getTextField2Type()); + } + } else if (updatedState.getTextField1() != null && updatedState.getTextField1().length() > 0) { + // If text 1 exists, put it in slot 1 formatted + tempString.append(" - ").append(updatedState.getTextField2()); + if (updatedState.getTextField2Type() != null) { + List<MetadataType> typeList = new ArrayList<>(); + typeList.add(updatedState.getTextField2Type()); + if (updatedState.getTextField1Type() != null) { + typeList.add(updatedState.getTextField1Type()); + } + tags.setMainField1(typeList); + } + } else { + // If text 1 does not exist, put it in slot 1 unformatted + tempString.append(updatedState.getTextField2()); + if (updatedState.getTextField2Type() != null) { + tags.setMainField1(updatedState.getTextField2Type()); + } + } + } + + // set mainField1 + show.setMainField1(tempString.toString()); + + // new stringBuilder object + tempString = new StringBuilder(); + + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + // If text 3 exists, put it in slot 2 + tempString.append(updatedState.getTextField3()); + if (updatedState.getTextField3Type() != null) { + List<MetadataType> typeList = new ArrayList<>(); + typeList.add(updatedState.getTextField3Type()); + tags.setMainField2(typeList); + } + } + + if (updatedState.getTextField4() != null && updatedState.getTextField4().length() > 0) { + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + // If text 3 exists, put it in slot 2 formatted + tempString.append(" - ").append(updatedState.getTextField4()); + if (updatedState.getTextField4Type() != null) { + List<MetadataType> typeList = new ArrayList<>(); + typeList.add(updatedState.getTextField4Type()); + if (updatedState.getTextField3Type() != null) { + typeList.add(updatedState.getTextField3Type()); + } + tags.setMainField2(typeList); + } + } else { + // If text 3 does not exist, put it in slot 3 unformatted + tempString.append(updatedState.getTextField4()); + if (updatedState.getTextField4Type() != null) { + tags.setMainField2(updatedState.getTextField4Type()); + } + } + } + + if (tempString.toString().length() > 0) { + show.setMainField2(tempString.toString()); + } + + show.setMetadataTags(tags); + return show; + } + + private Show assembleThreeLineShowText(Show show) { + MetadataTags tags = new MetadataTags(); + + if (updatedState.getTextField1() != null && updatedState.getTextField1().length() > 0) { + show.setMainField1(updatedState.getTextField1()); + if (updatedState.getTextField1Type() != null) { + tags.setMainField1(updatedState.getTextField1Type()); + } + } + + if (updatedState.getTextField2() != null && updatedState.getTextField2().length() > 0) { + show.setMainField2(updatedState.getTextField2()); + if (updatedState.getTextField2Type() != null) { + tags.setMainField2(updatedState.getTextField2Type()); + } + } + + StringBuilder tempString = new StringBuilder(); + + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + tempString.append(updatedState.getTextField3()); + if (updatedState.getTextField3Type() != null) { + tags.setMainField3(updatedState.getTextField3Type()); + } + } + + if (updatedState.getTextField4() != null && updatedState.getTextField4().length() > 0) { + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + // If text 3 exists, put it in slot 3 formatted + tempString.append(" - ").append(updatedState.getTextField4()); + if (updatedState.getTextField4Type() != null) { + List<MetadataType> tags4 = new ArrayList<>(); + if (updatedState.getTextField3Type() != null) { + tags4.add(updatedState.getTextField3Type()); + } + tags4.add(updatedState.getTextField4Type()); + tags.setMainField3(tags4); + } + } else { + // If text 3 does not exist, put it in slot 3 formatted + tempString.append(updatedState.getTextField4()); + if (updatedState.getTextField4Type() != null) { + tags.setMainField3(updatedState.getTextField4Type()); + } + } + } + show.setMainField3(tempString.toString()); + show.setMetadataTags(tags); + return show; + } + + private Show assembleFourLineShowText(Show show) { + MetadataTags tags = new MetadataTags(); + if (updatedState.getTextField1() != null && updatedState.getTextField1().length() > 0) { + show.setMainField1(updatedState.getTextField1()); + if (updatedState.getTextField1Type() != null) { + tags.setMainField1(updatedState.getTextField1Type()); + } + } + + if (updatedState.getTextField2() != null && updatedState.getTextField2().length() > 0) { + show.setMainField2(updatedState.getTextField2()); + if (updatedState.getTextField2Type() != null) { + tags.setMainField2(updatedState.getTextField2Type()); + } + } + + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + show.setMainField3(updatedState.getTextField3()); + if (updatedState.getTextField3Type() != null) { + tags.setMainField3(updatedState.getTextField3Type()); + } + } + + if (updatedState.getTextField4() != null && updatedState.getTextField4().length() > 0) { + show.setMainField4(updatedState.getTextField4()); + if (updatedState.getTextField4Type() != null) { + tags.setMainField4(updatedState.getTextField4Type()); + } + } + + show.setMetadataTags(tags); + + return show; + } + + // Extraction + + Show extractTextFromShow(Show show) { + Show newShow = new Show(); + newShow.setMainField1(show.getMainField1()); + newShow.setMainField2(show.getMainField2()); + newShow.setMainField3(show.getMainField3()); + newShow.setMainField4(show.getMainField4()); + newShow.setTemplateTitle(show.getTemplateTitle()); + newShow.setMetadataTags(show.getMetadataTags()); + newShow.setAlignment(show.getAlignment()); + + return newShow; + } + + private Show setBlankTextFields(Show newShow) { + newShow.setMainField1(""); + newShow.setMainField2(""); + newShow.setMainField3(""); + newShow.setMainField4(""); + newShow.setMediaTrack(""); + newShow.setTemplateTitle(""); + + return newShow; + } + + private void updateCurrentScreenDataFromShow(Show show) { + if (show == null) { + DebugTool.logError(TAG, "can not updateCurrentScreenDataFromShow from null show"); + return; + } + + // If the items are null, they were not updated, so we can't just set it directly + if (show.getMainField1() != null) { + currentScreenData.setMainField1(show.getMainField1()); + } + if (show.getMainField2() != null) { + currentScreenData.setMainField2(show.getMainField2()); + } + if (show.getMainField3() != null) { + currentScreenData.setMainField3(show.getMainField3()); + } + if (show.getMainField4() != null) { + currentScreenData.setMainField4(show.getMainField4()); + } + if (show.getTemplateTitle() != null) { + currentScreenData.setTemplateTitle(show.getTemplateTitle()); + } + if (show.getMediaTrack() != null) { + currentScreenData.setMediaTrack(show.getMediaTrack()); + } + if (show.getMetadataTags() != null) { + currentScreenData.setMetadataTags(show.getMetadataTags()); + } + if (show.getAlignment() != null) { + currentScreenData.setAlignment(show.getAlignment()); + } + if (show.getGraphic() != null) { + currentScreenData.setGraphic(show.getGraphic()); + } + if (show.getSecondaryGraphic() != null) { + currentScreenData.setSecondaryGraphic(show.getSecondaryGraphic()); + } + if (currentScreenDataUpdateListener != null) { + currentScreenDataUpdateListener.onUpdate(currentScreenData); + } + } + + // Helpers + + private List<String> findValidMainTextFields() { + List<String> array = new ArrayList<>(); + + if (updatedState.getTextField1() != null && updatedState.getTextField1().length() > 0) { + array.add(updatedState.getTextField1()); + } + + if (updatedState.getTextField2() != null && updatedState.getTextField2().length() > 0) { + array.add(updatedState.getTextField2()); + } + + if (updatedState.getTextField3() != null && updatedState.getTextField3().length() > 0) { + array.add(updatedState.getTextField3()); + } + + if (updatedState.getTextField4() != null && updatedState.getTextField4().length() > 0) { + array.add(updatedState.getTextField4()); + } + + return array; + } + + + private List<MetadataType> findNonNullMetadataFields() { + List<MetadataType> array = new ArrayList<>(); + + if (updatedState.getTextField1Type() != null) { + array.add(updatedState.getTextField1Type()); + } + + if (updatedState.getTextField2Type() != null) { + array.add(updatedState.getTextField2Type()); + } + + if (updatedState.getTextField3Type() != null) { + array.add(updatedState.getTextField3Type()); + } + + if (updatedState.getTextField4Type() != null) { + array.add(updatedState.getTextField4Type()); + } + + return array; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean sdlArtworkNeedsUpload(SdlArtwork artwork) { + if (fileManager.get() != null) { + return artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon(); + } + return false; + } + + /** + * Check to see if primaryGraphic should be updated + * + * @return true if primaryGraphic should be updated, false if not + */ + private boolean shouldUpdatePrimaryImage() { + boolean templateSupportsPrimaryArtwork = templateSupportsImageField(ImageFieldName.graphic); + String currentScreenDataPrimaryGraphicName = (currentScreenData != null && currentScreenData.getGraphic() != null) ? currentScreenData.getGraphic().getValue() : null; + String primaryGraphicName = updatedState.getPrimaryGraphic() != null ? updatedState.getPrimaryGraphic().getName() : null; + + boolean graphicMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataPrimaryGraphicName, primaryGraphicName, true, true); + + return templateSupportsPrimaryArtwork && !graphicMatchesExisting; + } + + /** + * Check to see if secondaryGraphic should be updated + * + * @return true if secondaryGraphic should be updated, false if not + */ + private boolean shouldUpdateSecondaryImage() { + boolean templateSupportsSecondaryArtwork = templateSupportsImageField(ImageFieldName.secondaryGraphic); + String currentScreenDataSecondaryGraphicName = (currentScreenData != null && currentScreenData.getSecondaryGraphic() != null) ? currentScreenData.getSecondaryGraphic().getValue() : null; + String secondaryGraphicName = updatedState.getSecondaryGraphic() != null ? updatedState.getSecondaryGraphic().getName() : null; + + boolean graphicMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataSecondaryGraphicName, secondaryGraphicName, true, true); + + // Cannot detect if there is a secondary image below v5.0, so we'll just try to detect if the primary image is allowed and allow the secondary image if it is. + if (internalInterface.get().getSdlMsgVersion().getMajorVersion() >= 5) { + return templateSupportsSecondaryArtwork && !graphicMatchesExisting; + } else { + return templateSupportsImageField(ImageFieldName.graphic) && !graphicMatchesExisting; + } + } + + /** + * Check to see if template supports the specified image field + * + * @return true if image field is supported, false if not + */ + private boolean templateSupportsImageField(ImageFieldName name) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); + } + + /** + * Check to see if mediaTrackTextField should be updated + * + * @return true if mediaTrackTextField should be updated, false if not + */ + private boolean shouldUpdateMediaTrackField() { + return templateSupportsTextField(TextFieldName.mediaTrack); + } + + /** + * Check to see if title should be updated + * + * @return true if title should be updated, false if not + */ + private boolean shouldUpdateTitleField() { + return templateSupportsTextField(TextFieldName.templateTitle); + } + + /** + * Check to see if field should be updated + * + * @return true if field should be updated, false if not + */ + private boolean templateSupportsTextField(TextFieldName name) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); + } + + Show getCurrentScreenData() { + return currentScreenData; + } + + void setCurrentScreenData(Show currentScreenData) { + this.currentScreenData = currentScreenData; + } + + private void finishOperation(boolean success) { + DebugTool.logInfo(TAG, "Finishing text and graphic update operation"); + if(listener != null){ + listener.onComplete(success); + } + onFinished(); + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java b/base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java new file mode 100644 index 000000000..bd6f749a3 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java @@ -0,0 +1,122 @@ +package com.smartdevicelink.managers.screen; + +import com.smartdevicelink.managers.file.filetypes.SdlArtwork; +import com.smartdevicelink.proxy.rpc.enums.MetadataType; +import com.smartdevicelink.proxy.rpc.enums.TextAlignment; + +class TextsAndGraphicsState { + private String textField1, textField2, textField3, textField4, mediaTrackTextField, title; + private MetadataType textField1Type, textField2Type, textField3Type, textField4Type; + private TextAlignment textAlignment; + private SdlArtwork primaryGraphic, secondaryGraphic; + + TextsAndGraphicsState(String textField1, String textField2, String textField3, String textField4, String mediaTrackTextField, + String title, SdlArtwork primaryGraphic, SdlArtwork secondaryGraphic, TextAlignment textAlignment, + MetadataType textField1Type, MetadataType textField2Type, MetadataType textField3Type, MetadataType textField4Type) { + this.textField1 = textField1; + this.textField2 = textField2; + this.textField3 = textField3; + this.textField4 = textField4; + this.mediaTrackTextField = mediaTrackTextField; + this.title = title; + this.primaryGraphic = primaryGraphic; + this.secondaryGraphic = secondaryGraphic; + this.textAlignment = textAlignment; + this.textField1Type = textField1Type; + this.textField2Type = textField2Type; + this.textField3Type = textField3Type; + this.textField4Type = textField4Type; + } + + String getTextField1() { + return textField1; + } + + void setTextField1(String textField1) { + this.textField1 = textField1; + } + + String getTextField2() { + return textField2; + } + + void setTextField2(String textField2) { + this.textField2 = textField2; + } + + String getTextField3() { + return textField3; + } + + void setTextField3(String textField3) { + this.textField3 = textField3; + } + + String getTextField4() { + return textField4; + } + + void setTextField4(String textField4) { + this.textField4 = textField4; + } + + String getMediaTrackTextField() { + return mediaTrackTextField; + } + + void setMediaTrackTextField(String mediaTrackTextField) { + this.mediaTrackTextField = mediaTrackTextField; + } + + String getTitle() { + return title; + } + + void setTitle(String title) { + this.title = title; + } + + MetadataType getTextField1Type() { + return textField1Type; + } + + void setTextField1Type(MetadataType textField1Type) { + this.textField1Type = textField1Type; + } + + MetadataType getTextField2Type() { + return textField2Type; + } + + void setTextField2Type(MetadataType textField2Type) { + this.textField2Type = textField2Type; + } + + MetadataType getTextField3Type() { + return textField3Type; + } + + void setTextField3Type(MetadataType textField3Type) { + this.textField3Type = textField3Type; + } + + MetadataType getTextField4Type() { + return textField4Type; + } + + void setTextField4Type(MetadataType textField4Type) { + this.textField4Type = textField4Type; + } + + TextAlignment getTextAlignment() { + return textAlignment; + } + + SdlArtwork getPrimaryGraphic() { + return primaryGraphic; + } + + SdlArtwork getSecondaryGraphic() { + return secondaryGraphic; + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java index 6585ad810..c85333cbe 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java @@ -35,13 +35,14 @@ package com.smartdevicelink.managers.screen.choiceset; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.smartdevicelink.proxy.TTSChunkFactory; import com.smartdevicelink.proxy.rpc.KeyboardProperties; import com.smartdevicelink.proxy.rpc.TTSChunk; import com.smartdevicelink.proxy.rpc.VrHelpItem; +import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities; import com.smartdevicelink.util.DebugTool; import java.util.ArrayList; +import java.util.Collections; import java.util.Hashtable; import java.util.List; @@ -108,15 +109,15 @@ public class ChoiceSet { // Help the dev by creating TTS chunks for them if (initialPrompt != null){ - setInitialPrompt(TTSChunkFactory.createSimpleTTSChunks(initialPrompt)); + setInitialPrompt(Collections.singletonList(new TTSChunk(initialPrompt, SpeechCapabilities.TEXT))); } if (timeoutPrompt != null){ - setTimeoutPrompt(TTSChunkFactory.createSimpleTTSChunks(timeoutPrompt)); + setTimeoutPrompt(Collections.singletonList(new TTSChunk(timeoutPrompt, SpeechCapabilities.TEXT))); } if (helpPrompt != null){ - setHelpPrompt(TTSChunkFactory.createSimpleTTSChunks(helpPrompt)); + setHelpPrompt(Collections.singletonList(new TTSChunk(helpPrompt, SpeechCapabilities.TEXT))); } // things to do @@ -397,5 +398,4 @@ public class ChoiceSet { } return clonedHelpItems; } - } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardAutocompleteCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardAutocompleteCompletionListener.java index 13b19238d..9178e6c74 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardAutocompleteCompletionListener.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardAutocompleteCompletionListener.java @@ -37,14 +37,6 @@ import java.util.List; public interface KeyboardAutocompleteCompletionListener { /** - * This listener is called when you wish to update your autocomplete text in response to the user's input - * @param updatedAutoCompleteText - The new autocomplete text to use - * @deprecated use {@link #onUpdatedAutoCompleteList(List<String>)} instead - */ - @Deprecated - void onUpdatedAutoCompleteText(String updatedAutoCompleteText); - - /** * This listener is called when you wish to update your autocomplete suggestions list in response to the user's input * @param updatedAutoCompleteList - The new autocomplete suggestions list to use */ diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java index 121b1f6c0..c0e206eed 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java @@ -341,15 +341,8 @@ class PresentChoiceSetOperation extends Task { // Notify of Keypress keyboardListener.updateAutocompleteWithInput(onKeyboard.getData(), new KeyboardAutocompleteCompletionListener() { @Override - public void onUpdatedAutoCompleteText(String updatedAutoCompleteText) { - keyboardProperties.setAutoCompleteText(updatedAutoCompleteText); - updateKeyboardProperties(null); - } - - @Override public void onUpdatedAutoCompleteList(List<String> updatedAutoCompleteList) { keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList<String>()); - keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null); updateKeyboardProperties(null); } }); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java index 33db35607..b48bbffd2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java @@ -286,12 +286,6 @@ class PresentKeyboardOperation extends Task { // Notify of Keypress keyboardListener.updateAutocompleteWithInput(onKeyboard.getData(), new KeyboardAutocompleteCompletionListener() { @Override - public void onUpdatedAutoCompleteText(String updatedAutoCompleteText) { - keyboardProperties.setAutoCompleteText(updatedAutoCompleteText); - updateKeyboardProperties(null); - } - - @Override public void onUpdatedAutoCompleteList(List<String> updatedAutoCompleteList) { keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList<String>()); keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 20cc44d38..4eef7777f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -116,24 +116,6 @@ public class MenuCell implements Cloneable{ * Creates a new MenuCell Object with multiple parameters set * <strong>NOTE: because this has sub-cells, there does not need to be a listener</strong> * @param title The cell's primary text - * @param icon The cell's image - * @param subCells The sub-cells for the sub menu that will appear when the cell is selected - * - * @deprecated use {@link #MenuCell(String, MenuLayout, SdlArtwork, List)} - */ - @Deprecated - public MenuCell(@NonNull String title, @Nullable SdlArtwork icon, @Nullable List<MenuCell> subCells) { - setTitle(title); // title is the only required param - setIcon(icon); - setSubCells(subCells); - setCellId(MAX_ID); - setParentCellId(MAX_ID); - } - - /** - * Creates a new MenuCell Object with multiple parameters set - * <strong>NOTE: because this has sub-cells, there does not need to be a listener</strong> - * @param title The cell's primary text * @param subMenuLayout The submenu's layout that the subCells will be shown in. If `null`, the * default submenu layout in the screen manager's `MenuConfiguration` will be used. * @param icon The cell's image |