Skip to content

Commit bc4906d

Browse files
authored
Merge pull request #455 from smartdevicelink/feature/soft-button-manager-queues
Feature/soft button manager queues
2 parents 0b123ab + f3f63a3 commit bc4906d

File tree

9 files changed

+871
-256
lines changed

9 files changed

+871
-256
lines changed

lib/js/src/manager/file/filetypes/SdlArtwork.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class SdlArtwork extends SdlFile {
7474
* @returns {SdlArtwork} - A reference to this instance to support method chaining.
7575
*/
7676
setType (fileType) {
77+
if (fileType === undefined) {
78+
return this;
79+
}
7780
if (fileType === null || fileType === FileType.GRAPHIC_JPEG || fileType === FileType.GRAPHIC_PNG
7881
|| fileType === FileType.GRAPHIC_BMP) {
7982
super.setType(fileType);

lib/js/src/manager/screen/_SoftButtonManagerBase.js

Lines changed: 176 additions & 213 deletions
Large diffs are not rendered by default.
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/*
2+
* Copyright (c) 2021, Livio, Inc.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following
13+
* disclaimer in the documentation and/or other materials provided with the
14+
* distribution.
15+
*
16+
* Neither the name of the Livio Inc. nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+
* POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
33+
import { _Task } from '../_Task';
34+
import { Show } from '../../rpc/messages/Show.js';
35+
import { SoftButton } from '../../rpc/structs/SoftButton.js';
36+
import { SoftButtonType } from '../../rpc/enums/SoftButtonType.js';
37+
38+
class _SoftButtonReplaceOperation extends _Task {
39+
/**
40+
* Initializes an instance of _SoftButtonReplaceOperation
41+
* @class
42+
* @private
43+
* @param {_LifecycleManager} lifecycleManager - A _LifecycleManager instance
44+
* @param {FileManager} fileManager - An instance of FileManager.
45+
* @param {SoftButtonCapabilities} softButtonCapabilities - The soft button capabilities
46+
* @param {SoftButtonObject[]} softButtonObjects - A list of soft button objects
47+
* @param {String} currentMainField1 - The main field value text shown on the head unit
48+
*/
49+
constructor (lifecycleManager, fileManager = null, softButtonCapabilities = null, softButtonObjects = null, currentMainField1 = null) {
50+
super('SoftButtonTransitionOperation');
51+
this._lifecycleManager = lifecycleManager;
52+
this._fileManager = fileManager;
53+
this._softButtonCapabilities = softButtonCapabilities;
54+
this._softButtonObjects = softButtonObjects;
55+
this._currentMainField1 = currentMainField1;
56+
}
57+
58+
/**
59+
* Sets the main field text
60+
* @param {String} currentMainField1 - The main field value text shown on the head unit
61+
*/
62+
setCurrentMainField1 (currentMainField1) {
63+
this._currentMainField1 = currentMainField1;
64+
}
65+
66+
/**
67+
* The method that causes the task to run.
68+
* @param {_Task} task - The task instance
69+
* @returns {Promise} - This promise does not resolve to any value
70+
*/
71+
async onExecute (task) {
72+
if (this.getState() === _Task.CANCELED) {
73+
this.onFinished();
74+
return;
75+
}
76+
77+
// Check the state of our images
78+
if (!this._supportsSoftButtonImages()) {
79+
// We don't support images at all
80+
console.warn('SoftButtonTransitionOperation - Soft button images are not supported. Attempting to send text-only soft buttons. If any button does not contain text, no buttons will be sent.');
81+
// Send text buttons if all the soft buttons have text
82+
const success = await this._sendCurrentStateTextOnlySoftButtons();
83+
if (!success) {
84+
console.error('SoftButtonTransitionOperation - Head unit does not support images and some of the soft buttons do not have text, so none of the buttons will be sent.');
85+
}
86+
} else if (this._currentStateHasImages() && !this._allCurrentStateImagesAreUploaded()) {
87+
// If there are images that aren't uploaded
88+
// Send text buttons if all the soft buttons have text
89+
await this._sendCurrentStateTextOnlySoftButtons();
90+
91+
// Upload initial images
92+
await this._uploadInitialStateImages();
93+
94+
// Send initial soft buttons w/ images
95+
await this._sendCurrentStateSoftButtons();
96+
97+
// Upload other images
98+
await this._uploadOtherStateImages();
99+
} else {
100+
// All the images are already uploaded. Send initial soft buttons w/ images.
101+
await this._sendCurrentStateSoftButtons();
102+
103+
await this._uploadOtherStateImages();
104+
}
105+
106+
this.onFinished();
107+
}
108+
109+
/**
110+
* 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.
111+
* @private
112+
* @returns {Promise} - Resolves to whether the operation is successful
113+
*/
114+
async _sendCurrentStateTextOnlySoftButtons () {
115+
if (this.getState() === _Task.CANCELED) {
116+
return false;
117+
}
118+
119+
console.log('SoftButtonTransitionOperation - Preparing to send text-only soft buttons');
120+
const textButtons = [];
121+
122+
for (const softButtonObject of this._softButtonObjects) {
123+
const softButton = softButtonObject.getCurrentStateSoftButton();
124+
if (softButton.getText() === null || softButton.getText() === undefined) {
125+
console.warn('SoftButtonTransitionOperation - Attempted to create text buttons, but some buttons don\'t support text, so no text-only soft buttons will be sent');
126+
return false;
127+
}
128+
// We should create a new softButtonObject rather than modifying the original one
129+
const textOnlySoftButton = new SoftButton()
130+
.setType(SoftButtonType.SBT_TEXT)
131+
.setText(softButton.getText())
132+
.setIsHighlighted(softButton.getIsHighlighted())
133+
.setSoftButtonID(softButton.getSoftButtonID())
134+
.setSystemAction(softButton.getSystemAction());
135+
136+
textButtons.push(textOnlySoftButton);
137+
}
138+
139+
if (this._lifecycleManager === null) {
140+
console.error('SoftButtonTransitionOperation: LifecycleManager is null');
141+
return false;
142+
}
143+
144+
const show = new Show()
145+
.setMainField1(this._currentMainField1)
146+
.setSoftButtons(textButtons);
147+
148+
const response = await this._lifecycleManager.sendRpcResolve(show);
149+
if (response.getSuccess()) {
150+
console.log('SoftButtonTransitionOperation - Finished sending text only soft buttons');
151+
} else {
152+
console.warn('SoftButtonTransitionOperation - Failed to update soft buttons with text buttons');
153+
}
154+
return response.getSuccess();
155+
}
156+
157+
/**
158+
* Uploads soft buttons representing the current states of the button objects
159+
* @private
160+
* @returns {Promise} - Resolves to whether the operation is successful
161+
*/
162+
async _sendCurrentStateSoftButtons () {
163+
if (this.getState() === _Task.CANCELED) {
164+
return false;
165+
}
166+
console.log('SoftButtonTransitionOperation - Preparing to send full soft buttons');
167+
const softButtons = [];
168+
169+
for (const softButtonObject of this._softButtonObjects) {
170+
softButtons.push(softButtonObject.getCurrentStateSoftButton());
171+
}
172+
173+
if (this._lifecycleManager === null) {
174+
console.error('SoftButtonTransitionOperation: LifecycleManager is null');
175+
return false;
176+
}
177+
178+
const show = new Show()
179+
.setMainField1(this._currentMainField1)
180+
.setSoftButtons(softButtons);
181+
182+
const response = await this._lifecycleManager.sendRpcResolve(show);
183+
if (response.getSuccess()) {
184+
console.log('SoftButtonTransitionOperation - Finished sending full soft buttons');
185+
} else {
186+
console.warn('SoftButtonTransitionOperation - Failed to update soft buttons');
187+
}
188+
return response.getSuccess();
189+
}
190+
191+
/**
192+
* Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready.
193+
* @private
194+
* @returns {Promise} - Resolves to whether the operation is successful
195+
*/
196+
async _uploadInitialStateImages () {
197+
if (this.getState() === _Task.CANCELED) {
198+
return false;
199+
}
200+
const initialStatesToBeUploaded = [];
201+
202+
for (const softButtonObject of this._softButtonObjects) {
203+
const softButtonState = softButtonObject.getCurrentState();
204+
if (softButtonState !== null && this._fileManager.fileNeedsUpload(softButtonState.getArtwork())) {
205+
initialStatesToBeUploaded.push(softButtonState.getArtwork());
206+
}
207+
}
208+
209+
if (initialStatesToBeUploaded.length === 0) {
210+
console.log('SoftButtonTransitionOperation: No initial state artworks to upload');
211+
return false;
212+
}
213+
214+
console.log('SoftButtonTransitionOperation: Uploading soft button initial artworks');
215+
if (this._fileManager === null) {
216+
return false;
217+
}
218+
219+
const successes = await this._fileManager.uploadArtworks(initialStatesToBeUploaded);
220+
if (successes.includes(false)) {
221+
console.error('SoftButtonTransitionOperation: Error uploading soft button artworks');
222+
} else {
223+
console.log('SoftButtonTransitionOperation: Soft button initial state artworks uploaded');
224+
}
225+
226+
return true;
227+
}
228+
229+
/**
230+
* Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready.
231+
* @private
232+
* @returns {Promise} - Resolves to whether the operation is successful
233+
*/
234+
async _uploadOtherStateImages () {
235+
if (this.getState() === _Task.CANCELED) {
236+
return false;
237+
}
238+
const otherStatesToBeUploaded = [];
239+
240+
for (const softButtonObject of this._softButtonObjects) {
241+
for (const softButtonState of softButtonObject.getStates()) {
242+
if (softButtonState.getName() !== softButtonObject.getCurrentState().getName() && this._fileManager.fileNeedsUpload(softButtonState.getArtwork())) {
243+
otherStatesToBeUploaded.push(softButtonState.getArtwork());
244+
}
245+
}
246+
}
247+
248+
if (otherStatesToBeUploaded.length === 0) {
249+
console.log('SoftButtonTransitionOperation: No other state artworks to upload');
250+
return false;
251+
}
252+
253+
console.log('SoftButtonTransitionOperation: Uploading soft button other state artworks');
254+
if (this._fileManager === null) {
255+
return false;
256+
}
257+
258+
const successes = await this._fileManager.uploadArtworks(otherStatesToBeUploaded);
259+
if (successes.includes(false)) {
260+
console.error('SoftButtonTransitionOperation: Error uploading soft button artworks');
261+
} else {
262+
console.log('SoftButtonTransitionOperation: Soft button other state artworks uploaded');
263+
}
264+
265+
return true;
266+
}
267+
268+
/**
269+
* Checks if soft button images are supported
270+
* @private
271+
* @returns {Boolean} - Whether soft button images are supported
272+
*/
273+
_supportsSoftButtonImages () {
274+
return this._softButtonCapabilities.getImageSupported();
275+
}
276+
277+
/**
278+
* Checks whether the soft button objects contain images
279+
* @private
280+
* @returns {Boolean} - Whether the soft button objects contain images
281+
*/
282+
_currentStateHasImages () {
283+
for (const softButtonObject of this._softButtonObjects) {
284+
if (softButtonObject.getCurrentState().getArtwork() !== null && softButtonObject.getCurrentState().getArtwork() !== undefined) {
285+
return true;
286+
}
287+
}
288+
return false;
289+
}
290+
291+
/**
292+
* Checks whether all soft button images are uploaded
293+
* @private
294+
* @returns {Boolean} - Whether all soft button images are uploaded
295+
*/
296+
_allCurrentStateImagesAreUploaded () {
297+
for (const softButtonObject of this._softButtonObjects) {
298+
const artwork = softButtonObject.getCurrentState().getArtwork();
299+
if (this._fileManager.fileNeedsUpload(artwork) && this._supportsSoftButtonImages()) {
300+
return false;
301+
}
302+
}
303+
return true;
304+
}
305+
}
306+
307+
export { _SoftButtonReplaceOperation };

0 commit comments

Comments
 (0)