diff options
| author | Bilal Alsharifi <bilal.alsharifi@gmail.com> | 2020-04-09 15:47:04 -0400 |
|---|---|---|
| committer | Bilal Alsharifi <bilal.alsharifi@gmail.com> | 2020-04-09 15:47:04 -0400 |
| commit | 1e7384a46bbe57be2555346c434ecab14be5d3e1 (patch) | |
| tree | e52785f5d32c66d008771aae8b83092a8889232e | |
| parent | 0d147c906d922042e9c6fae852c016ffe514d651 (diff) | |
| parent | 7bf138ffe0bf858f2de06696e04f5da7024d24d3 (diff) | |
| download | sdl_android-bugfix/issue_1331.tar.gz | |
Merge branch 'develop' into bugfix/issue_1331bugfix/issue_1331
# Conflicts:
# android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java
13 files changed, 578 insertions, 124 deletions
diff --git a/.gitignore b/.gitignore index 13eed80b8..20ec844f5 100644 --- a/.gitignore +++ b/.gitignore @@ -73,4 +73,6 @@ build/ /.idea/libraries /captures .externalNativeBuild - +gradle/ +gradlew +gradlew.bat diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java index 09cf35a82..bedeb0a39 100755 --- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java +++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlReceiver.java @@ -7,11 +7,9 @@ import android.util.Log; import com.smartdevicelink.transport.SdlBroadcastReceiver; import com.smartdevicelink.transport.SdlRouterService; -import com.smartdevicelink.transport.TransportConstants; public class SdlReceiver extends SdlBroadcastReceiver { private static final String TAG = "SdlBroadcastReciever"; - public static final String RECONNECT_LANG_CHANGE = "RECONNECT_LANG_CHANGE"; @Override public void onSdlEnabled(Context context, Intent intent) { @@ -36,16 +34,5 @@ public class SdlReceiver extends SdlBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); // Required if overriding this method - - if (intent != null) { - String action = intent.getAction(); - if (action != null){ - if(action.equalsIgnoreCase(TransportConstants.START_ROUTER_SERVICE_ACTION)) { - if (intent.getBooleanExtra(RECONNECT_LANG_CHANGE, false)) { - onSdlEnabled(context, intent); - } - } - } - } } }
\ No newline at end of file diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java index 5c6656bf9..63917067e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java @@ -25,6 +25,7 @@ import com.smartdevicelink.test.Test; import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.TCPTransportConfig; +import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -60,6 +61,8 @@ public class SdlManagerTests extends AndroidTestCase2 { public void setUp() throws Exception{ super.setUp(); + mTestContext = Mockito.mock(Context.class); + // set transport transport = new TCPTransportConfig(TCP_PORT, DEV_MACHINE_IP_ADDRESS, true); @@ -125,6 +128,7 @@ public class SdlManagerTests extends AndroidTestCase2 { builder.setLockScreenConfig(lockScreenConfig); builder.setMinimumProtocolVersion(Test.GENERAL_VERSION); builder.setMinimumRPCVersion(Test.GENERAL_VERSION); + builder.setContext(mTestContext); manager = builder.build(); // mock SdlProxyBase and set it manually diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManagerTests.java new file mode 100644 index 000000000..796f900d3 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManagerTests.java @@ -0,0 +1,105 @@ +package com.smartdevicelink.managers.lockscreen; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; + +import com.smartdevicelink.AndroidTestCase2; +import com.smartdevicelink.util.AndroidTools; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; + +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class LockScreenDeviceIconManagerTests extends AndroidTestCase2 { + + TemporaryFolder tempFolder = new TemporaryFolder(); + private LockScreenDeviceIconManager lockScreenDeviceIconManager; + private static final String ICON_URL = "https://i.imgur.com/TgkvOIZ.png"; + private static final String LAST_UPDATED_TIME = "lastUpdatedTime"; + private static final String STORED_PATH = "storedPath"; + + public void setup() throws Exception { + super.setUp(); + } + + public void tearDown() throws Exception { + super.tearDown(); + } + + public void testRetrieveIconShouldCallOnErrorTwiceWhenGivenURLThatCannotDownloadAndIconIsNotCached() { + final SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class); + final Context context = Mockito.mock(Context.class); + final LockScreenDeviceIconManager.OnIconRetrievedListener listener = Mockito.mock(LockScreenDeviceIconManager.OnIconRetrievedListener.class); + + Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); + Mockito.when(sharedPrefs.getString(anyString(), (String) isNull())).thenReturn(null); + + lockScreenDeviceIconManager = new LockScreenDeviceIconManager(context); + lockScreenDeviceIconManager.retrieveIcon("", listener); + verify(listener, times(2)).onError(anyString()); + } + + public void testRetrieveIconShouldCallOnImageOnImageRetrievedWithIconWhenIconUpdateTimeIsNullFromSharedPref() { + final SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class); + final Context context = Mockito.mock(Context.class); + final LockScreenDeviceIconManager.OnIconRetrievedListener listener = Mockito.mock(LockScreenDeviceIconManager.OnIconRetrievedListener.class); + + Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); + Mockito.when(sharedPrefs.getString(anyString(), (String) isNull())).thenReturn(null); + + lockScreenDeviceIconManager = new LockScreenDeviceIconManager(context); + lockScreenDeviceIconManager.retrieveIcon(ICON_URL, listener); + verify(listener, times(1)).onImageRetrieved((Bitmap) any()); + } + + + public void testRetrieveIconShouldCallOnImageOnImageRetrievedWithIconWhenCachedIconExpired() { + final SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class); + final Context context = Mockito.mock(Context.class); + final LockScreenDeviceIconManager.OnIconRetrievedListener listener = Mockito.mock(LockScreenDeviceIconManager.OnIconRetrievedListener.class); + + Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); + Mockito.when(sharedPrefs.getString(anyString(), (String) isNull())).thenReturn(daysToMillisecondsAsString(31)); + + lockScreenDeviceIconManager = new LockScreenDeviceIconManager(context); + lockScreenDeviceIconManager.retrieveIcon(ICON_URL, listener); + verify(listener, times(1)).onImageRetrieved((Bitmap) any()); + } + + public void testRetrieveIconShouldCallOnImageRetrievedWithIconWhenCachedIconIsUpToDate() { + final SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class); + final Context context = Mockito.mock(Context.class); + final SharedPreferences.Editor sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor.class); + final LockScreenDeviceIconManager.OnIconRetrievedListener listener = Mockito.mock(LockScreenDeviceIconManager.OnIconRetrievedListener.class); + + Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); + Mockito.when(sharedPrefs.getString(anyString(), (String) isNull())).thenReturn(daysToMillisecondsAsString(15)); + Mockito.when(sharedPrefs.edit()).thenReturn(sharedPrefsEditor); + Mockito.when(sharedPrefsEditor.clear()).thenReturn(sharedPrefsEditor); + + lockScreenDeviceIconManager = new LockScreenDeviceIconManager(context); + lockScreenDeviceIconManager.retrieveIcon(ICON_URL, listener); + verify(listener, times(1)).onImageRetrieved((Bitmap) any()); + } + + private String daysToMillisecondsAsString(int days) { + long milliSeconds = (long) days * 24 * 60 * 60 * 1000; + long previousDay = System.currentTimeMillis() - milliSeconds; + return String.valueOf(previousDay); + } +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java index 489964d7e..87bed3d1b 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java @@ -22,6 +22,7 @@ import com.smartdevicelink.proxy.rpc.DisplayCapability; import com.smartdevicelink.proxy.rpc.GetSystemCapability; import com.smartdevicelink.proxy.rpc.GetSystemCapabilityResponse; import com.smartdevicelink.proxy.rpc.HMICapabilities; +import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.OnSystemCapabilityUpdated; import com.smartdevicelink.proxy.rpc.PhoneCapability; import com.smartdevicelink.proxy.rpc.PresetBankCapabilities; @@ -34,7 +35,9 @@ import com.smartdevicelink.proxy.rpc.VideoStreamingCapability; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.WindowTypeCapabilities; import com.smartdevicelink.proxy.rpc.enums.AppServiceType; +import com.smartdevicelink.proxy.rpc.enums.AudioStreamingState; import com.smartdevicelink.proxy.rpc.enums.DisplayType; +import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities; import com.smartdevicelink.proxy.rpc.enums.ImageType; import com.smartdevicelink.proxy.rpc.enums.MediaClockFormat; @@ -44,6 +47,7 @@ import com.smartdevicelink.proxy.rpc.enums.Result; import com.smartdevicelink.proxy.rpc.enums.ServiceUpdateReason; import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; +import com.smartdevicelink.proxy.rpc.enums.SystemContext; import com.smartdevicelink.proxy.rpc.enums.WindowType; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener; @@ -67,6 +71,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -216,6 +221,11 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { request.getOnRPCResponseListener().onResponse(CorrelationIdGenerator.generateId(), response); } } + + @Override + public void addOnRPCListener(FunctionID messageId, OnRPCListener listener) { + listener.onReceived(new OnHMIStatus(HMILevel.HMI_FULL, AudioStreamingState.NOT_AUDIBLE, SystemContext.SYSCTXT_MAIN)); + } }); systemCapabilityManager.getCapability(SystemCapabilityType.VIDEO_STREAMING, new OnSystemCapabilityListener() { @@ -234,6 +244,21 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { }); } + private Answer<Void> createOnHMIStatusAnswer(final HMILevel hmiLevel){ + Answer<Void> onHMIStatusAnswer = new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + OnRPCListener onHMIStatusListener = (OnRPCListener) args[1]; + OnHMIStatus onHMIStatusFakeNotification = new OnHMIStatus(); + onHMIStatusFakeNotification.setHmiLevel(hmiLevel); + onHMIStatusListener.onReceived(onHMIStatusFakeNotification); + return null; + } + }; + return onHMIStatusAnswer; + } + private Answer<Void> createOnSendGetSystemCapabilityAnswer (final boolean success, final Boolean subscribe) { Answer<Void> onSendGetSystemCapabilityAnswer = new Answer<Void>() { @Override @@ -266,6 +291,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { // Test case 1 (capability not cached, listener not null, forceUpdate false) internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); scm = new SystemCapabilityManager(internalInterface); onSystemCapabilityListener = mock(OnSystemCapabilityListener.class); doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); @@ -278,6 +304,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { // Test case 2 (capability cached, listener not null, forceUpdate true) internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); scm = new SystemCapabilityManager(internalInterface); onSystemCapabilityListener = mock(OnSystemCapabilityListener.class); doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); @@ -290,6 +317,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { // Test case 3 (capability cached, listener null, forceUpdate true) internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); scm = new SystemCapabilityManager(internalInterface); onSystemCapabilityListener = null; doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); @@ -301,6 +329,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { // Test case 4 (capability cached, listener null, forceUpdate false) internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); scm = new SystemCapabilityManager(internalInterface); onSystemCapabilityListener = null; doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); @@ -310,10 +339,25 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { verify(internalInterface, times(0)).sendRPC(any(GetSystemCapability.class)); } + public void testGetCapabilityHmiNone() { + ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_NONE)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); + SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); + OnSystemCapabilityListener onSystemCapabilityListener = mock(OnSystemCapabilityListener.class); + doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); + scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, null); + VideoStreamingCapability retrievedCapability = (VideoStreamingCapability) scm.getCapability(SystemCapabilityType.VIDEO_STREAMING, onSystemCapabilityListener, false); + assertNull(retrievedCapability); + verify(internalInterface, times(0)).sendRPC(any(GetSystemCapability.class)); + verify(onSystemCapabilityListener, times(0)).onCapabilityRetrieved(any(Object.class)); + verify(onSystemCapabilityListener, times(1)).onError(any(String.class)); + } + public void testAddOnSystemCapabilityListenerWithSubscriptionsSupportedAndCapabilityCached() { SdlMsgVersion sdlMsgVersion = new SdlMsgVersion(6, 0); // This version supports capability subscriptions sdlMsgVersion.setPatchVersion(0); ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); when(internalInterface.getSdlMsgVersion()).thenReturn(sdlMsgVersion); SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, videoStreamingCapability); @@ -359,6 +403,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { SdlMsgVersion sdlMsgVersion = new SdlMsgVersion(6, 0); // This version supports capability subscriptions sdlMsgVersion.setPatchVersion(0); ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); when(internalInterface.getSdlMsgVersion()).thenReturn(sdlMsgVersion); SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, null); @@ -404,6 +449,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { SdlMsgVersion sdlMsgVersion = new SdlMsgVersion(5, 0); // This version doesn't support capability subscriptions sdlMsgVersion.setPatchVersion(0); ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); when(internalInterface.getSdlMsgVersion()).thenReturn(sdlMsgVersion); SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, videoStreamingCapability); @@ -449,6 +495,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { SdlMsgVersion sdlMsgVersion = new SdlMsgVersion(5, 0); // This version doesn't support capability subscriptions sdlMsgVersion.setPatchVersion(0); ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); when(internalInterface.getSdlMsgVersion()).thenReturn(sdlMsgVersion); SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, null); @@ -494,6 +541,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { SdlMsgVersion sdlMsgVersion = new SdlMsgVersion(5, 0); // This version doesn't support capability subscriptions sdlMsgVersion.setPatchVersion(0); ISdl internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); when(internalInterface.getSdlMsgVersion()).thenReturn(sdlMsgVersion); SystemCapabilityManager scm = new SystemCapabilityManager(internalInterface); scm.setCapability(SystemCapabilityType.VIDEO_STREAMING, videoStreamingCapability); @@ -586,8 +634,8 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { // Test case 1 (capability cached, listener not null, forceUpdate true) - // Force updating DISPLAYS capability should call onError() internalInterface = mock(ISdl.class); + doAnswer(createOnHMIStatusAnswer(HMILevel.HMI_FULL)).when(internalInterface).addOnRPCListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCListener.class)); scm = new SystemCapabilityManager(internalInterface); onSystemCapabilityListener = mock(OnSystemCapabilityListener.class); doAnswer(createOnSendGetSystemCapabilityAnswer(true, null)).when(internalInterface).sendRPC(any(GetSystemCapability.class)); @@ -595,8 +643,8 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { retrievedCapability = (DisplayCapabilities) scm.getCapability(SystemCapabilityType.DISPLAYS, onSystemCapabilityListener, true); assertNotNull(retrievedCapability); verify(internalInterface, times(0)).sendRPC(any(GetSystemCapability.class)); - verify(onSystemCapabilityListener, times(0)).onCapabilityRetrieved(any(Object.class)); - verify(onSystemCapabilityListener, times(1)).onError(any(String.class)); + verify(onSystemCapabilityListener, times(1)).onCapabilityRetrieved(any(Object.class)); + verify(onSystemCapabilityListener, times(0)).onError(any(String.class)); // Test case 2 (Add listener) @@ -683,7 +731,6 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { } public void testOnSystemCapabilityUpdated(){ - InternalSDLInterface iSDL = new InternalSDLInterface(); String baseName = "NavTest", baseID = "37F98053AE"; AppServiceCapability capability1 = AppServiceFactory.createAppServiceCapability(AppServiceType.NAVIGATION, baseName, null, true, null); @@ -948,13 +995,21 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { public Object getCapability(SystemCapabilityType systemCapabilityType){return null;} @Override - public void getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener) { } + public void getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener) { + } @Override - public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse(){return null;} + public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() { + return null; + } + + @Override + public Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate) { + return null; + } @Override - public SdlMsgVersion getSdlMsgVersion(){ + public SdlMsgVersion getSdlMsgVersion() { return null; } @@ -1001,6 +1056,4 @@ public class SystemCapabilityManagerTests extends AndroidTestCase2 { @Override public void startRPCEncryption() {} } - - } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java index f87839254..b0ff164cd 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java @@ -872,6 +872,14 @@ public class SdlManager extends BaseSdlManager{ } @Override + public Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate) { + if (proxy != null && proxy.getSystemCapabilityManager() != null) { + return proxy.getSystemCapabilityManager().getCapability(systemCapabilityType, scListener, forceUpdate); + } + return null; + } + + @Override public boolean isCapabilitySupported(SystemCapabilityType systemCapabilityType){ return proxy.isCapabilitySupported(systemCapabilityType); } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManager.java new file mode 100644 index 000000000..b2b8e6b14 --- /dev/null +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManager.java @@ -0,0 +1,214 @@ +package com.smartdevicelink.managers.lockscreen; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import com.smartdevicelink.util.AndroidTools; +import com.smartdevicelink.util.DebugTool; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * <strong>LockScreenDeviceIconManager</strong> <br> + * + * The LockScreenDeviceIconManager handles the logic of caching and retrieving cached lock screen icons <br> + * + */ +class LockScreenDeviceIconManager { + + private Context context; + private static final String SDL_DEVICE_STATUS_SHARED_PREFS = "sdl.lockScreenIcon"; + private static final String STORED_ICON_DIRECTORY_PATH = "sdl/lock_screen_icon/"; + + interface OnIconRetrievedListener { + void onImageRetrieved(Bitmap icon); + void onError(String info); + } + + LockScreenDeviceIconManager(Context context) { + this.context = context; + File lockScreenDirectory = new File(context.getCacheDir(), STORED_ICON_DIRECTORY_PATH); + lockScreenDirectory.mkdirs(); + } + + /** + * Will try to return a lock screen icon either from cache or downloaded + * if it fails iconRetrievedListener.OnError will be called with corresponding error message + * @param iconURL url that the lock screen icon is downloaded from + * @param iconRetrievedListener an interface that will implement onIconReceived and OnError methods + */ + void retrieveIcon(String iconURL, OnIconRetrievedListener iconRetrievedListener) { + Bitmap icon = null; + try { + if (isIconCachedAndValid(iconURL)) { + DebugTool.logInfo("Icon Is Up To Date"); + icon = getFileFromCache(iconURL); + if (icon == null) { + DebugTool.logInfo("Icon from cache was null, attempting to re-download"); + icon = AndroidTools.downloadImage(iconURL); + if (icon != null) { + saveFileToCache(icon, iconURL); + } else { + iconRetrievedListener.onError("Icon downloaded was null"); + return; + } + } + iconRetrievedListener.onImageRetrieved(icon); + } else { + // The icon is unknown or expired. Download the image, save it to the cache, and update the archive file + DebugTool.logInfo("Lock Screen Icon Update Needed"); + icon = AndroidTools.downloadImage(iconURL); + if (icon != null) { + saveFileToCache(icon, iconURL); + iconRetrievedListener.onImageRetrieved(icon); + } else { + iconRetrievedListener.onError("Icon downloaded was null"); + } + } + } catch (IOException e) { + iconRetrievedListener.onError("device Icon Error Downloading, Will attempt to grab cached Icon even if expired: \n" + e.toString()); + icon = getFileFromCache(iconURL); + if (icon != null) { + iconRetrievedListener.onImageRetrieved(icon); + } else { + iconRetrievedListener.onError("Unable to retrieve icon from cache"); + } + } + } + + /** + * Will decide if a cached icon is available and up to date + * @param iconUrl url will be hashed and used to look up last updated timestamp in shared preferences + * @return True when icon details are in shared preferences and less than 30 days old, False if icon details are too old or not found + */ + private boolean isIconCachedAndValid(String iconUrl) { + String iconHash = getMD5HashFromIconUrl(iconUrl); + SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE); + String iconLastUpdatedTime = sharedPref.getString(iconHash, null); + if(iconLastUpdatedTime == null) { + DebugTool.logInfo("No Icon Details Found In Shared Preferences"); + return false; + } else { + DebugTool.logInfo("Icon Details Found"); + long lastUpdatedTime = 0; + try { + lastUpdatedTime = Long.parseLong(iconLastUpdatedTime); + } catch (NumberFormatException e) { + DebugTool.logInfo("Invalid time stamp stored to shared preferences, clearing cache and share preferences"); + clearIconDirectory(); + sharedPref.edit().clear().commit(); + } + long currentTime = System.currentTimeMillis(); + + long timeDifference = currentTime - lastUpdatedTime; + long daysBetweenLastUpdate = timeDifference / (1000 * 60 * 60 * 24); + return daysBetweenLastUpdate < 30; + } + } + + /** + * Will try to save icon to cache + * @param icon the icon bitmap that should be saved to cache + * @param iconUrl the url where the icon was retrieved will be hashed and used for file and file details lookup + */ + private void saveFileToCache(Bitmap icon, String iconUrl) { + String iconHash = getMD5HashFromIconUrl(iconUrl); + File f = new File(this.context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH, iconHash); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + icon.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos); + byte[] bitmapData = bos.toByteArray(); + + FileOutputStream fos = null; + try { + fos = new FileOutputStream(f); + fos.write(bitmapData); + fos.flush(); + fos.close(); + writeDeviceIconParametersToSharedPreferences(iconHash); + } catch (Exception e) { + DebugTool.logError("Failed to save icon to cache"); + e.printStackTrace(); + } + } + + /** + * Will try to retrieve icon bitmap from cached directory + * @param iconUrl the url where the icon was retrieved will be hashed and used to look up file location + * @return bitmap of device icon or null if it fails to find the icon or read from shared preferences + */ + private Bitmap getFileFromCache(String iconUrl) { + String iconHash = getMD5HashFromIconUrl(iconUrl); + SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE); + String iconLastUpdatedTime = sharedPref.getString(iconHash, null); + + if (iconLastUpdatedTime != null) { + Bitmap cachedIcon = BitmapFactory.decodeFile(this.context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH + "/" + iconHash); + if(cachedIcon == null) { + DebugTool.logError("Failed to get Bitmap from decoding file cache"); + clearIconDirectory(); + sharedPref.edit().clear().commit(); + return null; + } else { + return cachedIcon; + } + } else { + DebugTool.logError("Failed to get shared preferences"); + return null; + } + } + + /** + * Will write information about the icon to shared preferences + * icon information will have a look up key of the hashed icon url and the current timestamp to indicated when the icon was last updated. + * @param iconHash the url where the icon was retrieved will be hashed and used lookup key + */ + private void writeDeviceIconParametersToSharedPreferences(String iconHash) { + SharedPreferences sharedPref = this.context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(iconHash, String.valueOf(System.currentTimeMillis())); + editor.commit(); + } + + /** + * Create an MD5 hash of the icon url for file storage and lookup/shared preferences look up + * @param iconUrl the url where the icon was retrieved + * @return MD5 hash of the icon URL + */ + private String getMD5HashFromIconUrl(String iconUrl) { + String iconHash = null; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(iconUrl.getBytes()); + BigInteger no = new BigInteger(1, messageDigest); + String hashtext = no.toString(16); + while (hashtext.length() < 32) { + hashtext = "0" + hashtext; + } + iconHash = hashtext; + } catch (NoSuchAlgorithmException e) { + DebugTool.logError("Unable to hash icon url"); + e.printStackTrace(); + } + return iconHash; + } + + /** + * Clears all files in the directory where lock screen icons are cached + */ + private void clearIconDirectory() { + File iconDir = new File(context.getCacheDir() + "/" + STORED_ICON_DIRECTORY_PATH); + if (iconDir.listFiles() != null) { + for (File child : iconDir.listFiles()) { + child.delete(); + } + } + } +} diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java index 30ed1b575..2e81894ed 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java @@ -54,9 +54,8 @@ import com.smartdevicelink.proxy.rpc.enums.LockScreenStatus; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.RequestType; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; -import com.smartdevicelink.util.AndroidTools; +import com.smartdevicelink.util.DebugTool; -import java.io.IOException; import java.lang.ref.WeakReference; /** @@ -82,11 +81,14 @@ public class LockScreenManager extends BaseSubManager { private boolean mLockScreenHasBeenDismissed, lockscreenDismissReceiverRegistered, receivedFirstDDNotification; private String mLockscreenWarningMsg; private BroadcastReceiver mLockscreenDismissedReceiver; + private LockScreenDeviceIconManager mLockScreenDeviceIconManager; public LockScreenManager(LockScreenConfig lockScreenConfig, Context context, ISdl internalInterface){ super(internalInterface); this.context = new WeakReference<>(context); + this.mLockScreenDeviceIconManager = new LockScreenDeviceIconManager(context); + // set initial class variables hmiLevel = HMILevel.HMI_NONE; @@ -231,7 +233,7 @@ public class LockScreenManager extends BaseSubManager { if (msg.getRequestType() == RequestType.LOCK_SCREEN_ICON_URL && msg.getUrl() != null) { // send intent to activity to download icon from core - deviceIconUrl = msg.getUrl(); + deviceIconUrl = msg.getUrl().replace("http://", "https://"); downloadDeviceIcon(deviceIconUrl); } } @@ -375,17 +377,25 @@ public class LockScreenManager extends BaseSubManager { new Thread(new Runnable(){ @Override public void run(){ - try{ - deviceLogo = AndroidTools.downloadImage(url); - Intent intent = new Intent(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_DOWNLOADED); - intent.putExtra(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_EXTRA, deviceLogoEnabled); - intent.putExtra(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_BITMAP, deviceLogo); - if (context.get() != null) { - context.get().sendBroadcast(intent); + mLockScreenDeviceIconManager.retrieveIcon(url, new LockScreenDeviceIconManager.OnIconRetrievedListener() { + @Override + public void onImageRetrieved(Bitmap icon) { + deviceLogo = icon; + if(deviceLogo != null) { + Intent intent = new Intent(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_DOWNLOADED); + intent.putExtra(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_EXTRA, deviceLogoEnabled); + intent.putExtra(SDLLockScreenActivity.LOCKSCREEN_DEVICE_LOGO_BITMAP, deviceLogo); + if (context.get() != null) { + context.get().sendBroadcast(intent); + } + } } - }catch(IOException e){ - Log.e(TAG, "device Icon Error Downloading"); - } + + @Override + public void onError(String info) { + DebugTool.logError(info); + } + }); } }).start(); } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java index 8fc5edb2c..b6f49a9a4 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java @@ -147,12 +147,15 @@ public class VideoStreamManager extends BaseVideoStreamManager { @Override public void onNotified(RPCNotification notification) { if(notification != null){ - OnHMIStatus onHMIStatus = (OnHMIStatus)notification; + OnHMIStatus onHMIStatus = (OnHMIStatus) notification; if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { return; } OnHMIStatus prevOnHMIStatus = currentOnHMIStatus; currentOnHMIStatus = onHMIStatus; + if (!HMILevel.HMI_NONE.equals(currentOnHMIStatus.getHmiLevel()) && VideoStreamManager.this.parameters == null) { + getVideoStreamingParams(); + } checkState(); if (hasStarted && (isHMIStateVideoStreamCapable(prevOnHMIStatus)) && (!isHMIStateVideoStreamCapable(currentOnHMIStatus))) { internalInterface.stopVideoService(); @@ -198,7 +201,6 @@ public class VideoStreamManager extends BaseVideoStreamManager { @Override public void start(CompletionListener listener) { isTransportAvailable = internalInterface.isTransportForServiceAvailable(SessionType.NAV); - getVideoStreamingParams(); checkState(); super.start(listener); } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java b/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java index c69011e13..ad9969163 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java @@ -455,6 +455,14 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase> }
@Override
+ public Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate) {
+ if (_systemCapabilityManager != null) {
+ return _systemCapabilityManager.getCapability(systemCapabilityType, scListener, forceUpdate);
+ }
+ return null;
+ }
+
+ @Override
public SdlMsgVersion getSdlMsgVersion(){
try {
return SdlProxyBase.this.getSdlMsgVersion();
diff --git a/base/src/main/java/com/smartdevicelink/proxy/SystemCapabilityManager.java b/base/src/main/java/com/smartdevicelink/proxy/SystemCapabilityManager.java index 0c90bde84..b45c3634a 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/SystemCapabilityManager.java +++ b/base/src/main/java/com/smartdevicelink/proxy/SystemCapabilityManager.java @@ -42,6 +42,7 @@ import com.smartdevicelink.proxy.rpc.DisplayCapability; import com.smartdevicelink.proxy.rpc.GetSystemCapability; import com.smartdevicelink.proxy.rpc.GetSystemCapabilityResponse; import com.smartdevicelink.proxy.rpc.HMICapabilities; +import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.OnSystemCapabilityUpdated; import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse; import com.smartdevicelink.proxy.rpc.SdlMsgVersion; @@ -51,6 +52,7 @@ import com.smartdevicelink.proxy.rpc.SystemCapability; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.WindowTypeCapabilities; import com.smartdevicelink.proxy.rpc.enums.DisplayType; +import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.ImageType; import com.smartdevicelink.proxy.rpc.enums.MediaClockFormat; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; @@ -78,6 +80,7 @@ public class SystemCapabilityManager { private final ISdl callback; private OnRPCListener rpcListener; private boolean shouldConvertDeprecatedDisplayCapabilities; + private HMILevel currentHMILevel; public SystemCapabilityManager(ISdl callback) { this.callback = callback; @@ -85,7 +88,9 @@ public class SystemCapabilityManager { this.onSystemCapabilityListeners = new HashMap<>(); this.cachedSystemCapabilities = new HashMap<>(); this.systemCapabilitiesSubscriptionStatus = new HashMap<>(); + this.systemCapabilitiesSubscriptionStatus.put(SystemCapabilityType.DISPLAYS, true); this.shouldConvertDeprecatedDisplayCapabilities = true; + this.currentHMILevel = HMILevel.HMI_NONE; setupRpcListeners(); } @@ -255,82 +260,89 @@ public class SystemCapabilityManager { } } - private void setupRpcListeners() { - rpcListener = new OnRPCListener() { - @Override - public void onReceived(RPCMessage message) { - if (message != null) { - if (RPCMessage.KEY_RESPONSE.equals(message.getMessageType())) { - switch (message.getFunctionID()) { - case SET_DISPLAY_LAYOUT: - SetDisplayLayoutResponse response = (SetDisplayLayoutResponse) message; - setCapability(SystemCapabilityType.DISPLAY, response.getDisplayCapabilities()); - setCapability(SystemCapabilityType.BUTTON, response.getButtonCapabilities()); - setCapability(SystemCapabilityType.PRESET_BANK, response.getPresetBankCapabilities()); - setCapability(SystemCapabilityType.SOFTBUTTON, response.getSoftButtonCapabilities()); - if (shouldConvertDeprecatedDisplayCapabilities) { - setCapability(SystemCapabilityType.DISPLAYS, createDisplayCapabilityList(response)); - } - break; - case GET_SYSTEM_CAPABILITY: - GetSystemCapabilityResponse systemCapabilityResponse = (GetSystemCapabilityResponse) message; - SystemCapability systemCapability = systemCapabilityResponse.getSystemCapability(); - if (systemCapabilityResponse.getSuccess() && SystemCapabilityType.DISPLAYS.equals(systemCapability.getSystemCapabilityType())) { - shouldConvertDeprecatedDisplayCapabilities = false; // Successfully got DISPLAYS data. No conversion needed anymore - List<DisplayCapability> newCapabilities = (List<DisplayCapability>) systemCapability.getCapabilityForType(SystemCapabilityType.DISPLAYS); - updateCachedDisplayCapabilityList(newCapabilities); - } - break; - } - } else if (RPCMessage.KEY_NOTIFICATION.equals(message.getMessageType())) { - switch (message.getFunctionID()) { - case ON_SYSTEM_CAPABILITY_UPDATED: - OnSystemCapabilityUpdated onSystemCapabilityUpdated = (OnSystemCapabilityUpdated) message; - if (onSystemCapabilityUpdated.getSystemCapability() != null) { - SystemCapability systemCapability = onSystemCapabilityUpdated.getSystemCapability(); - SystemCapabilityType systemCapabilityType = systemCapability.getSystemCapabilityType(); - Object capability = systemCapability.getCapabilityForType(systemCapabilityType); - if (cachedSystemCapabilities.containsKey(systemCapabilityType)) { //The capability already exists - switch (systemCapabilityType) { - case APP_SERVICES: - // App services only updates what was changed so we need - // to update the capability rather than override it - AppServicesCapabilities appServicesCapabilities = (AppServicesCapabilities) capability; - if (capability != null) { - List<AppServiceCapability> appServicesCapabilitiesList = appServicesCapabilities.getAppServices(); - AppServicesCapabilities cachedAppServicesCapabilities = (AppServicesCapabilities) cachedSystemCapabilities.get(systemCapabilityType); - //Update the cached app services - if (cachedAppServicesCapabilities != null) { - cachedAppServicesCapabilities.updateAppServices(appServicesCapabilitiesList); - } - //Set the new capability object to the updated cached capabilities - capability = cachedAppServicesCapabilities; - } - break; - case DISPLAYS: - shouldConvertDeprecatedDisplayCapabilities = false; // Successfully got DISPLAYS data. No conversion needed anymore - // this notification can return only affected windows (hence not all windows) - List<DisplayCapability> newCapabilities = (List<DisplayCapability>) capability; - updateCachedDisplayCapabilityList(newCapabilities); - systemCapabilitiesSubscriptionStatus.put(SystemCapabilityType.DISPLAYS, true); - } - } - if (capability != null) { - setCapability(systemCapabilityType, capability); - } - } - } - } - } - } - }; - - if (callback != null) { - callback.addOnRPCListener(FunctionID.GET_SYSTEM_CAPABILITY, rpcListener); - callback.addOnRPCListener(FunctionID.SET_DISPLAY_LAYOUT, rpcListener); - callback.addOnRPCListener(FunctionID.ON_SYSTEM_CAPABILITY_UPDATED, rpcListener); - } - } + private void setupRpcListeners() { + rpcListener = new OnRPCListener() { + @Override + public void onReceived(RPCMessage message) { + if (message != null) { + if (RPCMessage.KEY_RESPONSE.equals(message.getMessageType())) { + switch (message.getFunctionID()) { + case SET_DISPLAY_LAYOUT: + SetDisplayLayoutResponse response = (SetDisplayLayoutResponse) message; + setCapability(SystemCapabilityType.DISPLAY, response.getDisplayCapabilities()); + setCapability(SystemCapabilityType.BUTTON, response.getButtonCapabilities()); + setCapability(SystemCapabilityType.PRESET_BANK, response.getPresetBankCapabilities()); + setCapability(SystemCapabilityType.SOFTBUTTON, response.getSoftButtonCapabilities()); + if (shouldConvertDeprecatedDisplayCapabilities) { + setCapability(SystemCapabilityType.DISPLAYS, createDisplayCapabilityList(response)); + } + break; + case GET_SYSTEM_CAPABILITY: + GetSystemCapabilityResponse systemCapabilityResponse = (GetSystemCapabilityResponse) message; + SystemCapability systemCapability = systemCapabilityResponse.getSystemCapability(); + if (systemCapabilityResponse.getSuccess() && SystemCapabilityType.DISPLAYS.equals(systemCapability.getSystemCapabilityType())) { + shouldConvertDeprecatedDisplayCapabilities = false; // Successfully got DISPLAYS data. No conversion needed anymore + List<DisplayCapability> newCapabilities = (List<DisplayCapability>) systemCapability.getCapabilityForType(SystemCapabilityType.DISPLAYS); + updateCachedDisplayCapabilityList(newCapabilities); + } + break; + } + } else if (RPCMessage.KEY_NOTIFICATION.equals(message.getMessageType())) { + switch (message.getFunctionID()) { + case ON_HMI_STATUS: + OnHMIStatus onHMIStatus = (OnHMIStatus) message; + if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { + return; + } + currentHMILevel = onHMIStatus.getHmiLevel(); + break; + case ON_SYSTEM_CAPABILITY_UPDATED: + OnSystemCapabilityUpdated onSystemCapabilityUpdated = (OnSystemCapabilityUpdated) message; + if (onSystemCapabilityUpdated.getSystemCapability() != null) { + SystemCapability systemCapability = onSystemCapabilityUpdated.getSystemCapability(); + SystemCapabilityType systemCapabilityType = systemCapability.getSystemCapabilityType(); + Object capability = systemCapability.getCapabilityForType(systemCapabilityType); + if (cachedSystemCapabilities.containsKey(systemCapabilityType)) { //The capability already exists + switch (systemCapabilityType) { + case APP_SERVICES: + // App services only updates what was changed so we need + // to update the capability rather than override it + AppServicesCapabilities appServicesCapabilities = (AppServicesCapabilities) capability; + if (capability != null) { + List<AppServiceCapability> appServicesCapabilitiesList = appServicesCapabilities.getAppServices(); + AppServicesCapabilities cachedAppServicesCapabilities = (AppServicesCapabilities) cachedSystemCapabilities.get(systemCapabilityType); + //Update the cached app services + if (cachedAppServicesCapabilities != null) { + cachedAppServicesCapabilities.updateAppServices(appServicesCapabilitiesList); + } + //Set the new capability object to the updated cached capabilities + capability = cachedAppServicesCapabilities; + } + break; + case DISPLAYS: + shouldConvertDeprecatedDisplayCapabilities = false; // Successfully got DISPLAYS data. No conversion needed anymore + // this notification can return only affected windows (hence not all windows) + List<DisplayCapability> newCapabilities = (List<DisplayCapability>) capability; + updateCachedDisplayCapabilityList(newCapabilities); + } + } + if (capability != null) { + setCapability(systemCapabilityType, capability); + } + } + } + } + } + } + }; + + if (callback != null) { + callback.addOnRPCListener(FunctionID.GET_SYSTEM_CAPABILITY, rpcListener); + callback.addOnRPCListener(FunctionID.SET_DISPLAY_LAYOUT, rpcListener); + callback.addOnRPCListener(FunctionID.ON_SYSTEM_CAPABILITY_UPDATED, rpcListener); + callback.addOnRPCListener(FunctionID.ON_HMI_STATUS, rpcListener); + } + } /** * Sets a capability in the cached map. This should only be done when an RPC is received and contains updates to the capability @@ -452,8 +464,10 @@ public class SystemCapabilityManager { private Object getCapabilityPrivate(final SystemCapabilityType systemCapabilityType, final OnSystemCapabilityListener scListener, final Boolean subscribe, final boolean forceUpdate) { Object cachedCapability = cachedSystemCapabilities.get(systemCapabilityType); + // No need to force update if the app is subscribed to that type because updated values will be received via notifications anyway + boolean shouldForceUpdate = forceUpdate && !isSubscribedToSystemCapability(systemCapabilityType); boolean shouldUpdateSystemCapabilitySubscription = (subscribe != null) && (subscribe != isSubscribedToSystemCapability(systemCapabilityType)) && supportsSubscriptions(); - boolean shouldSendGetCapabilityRequest = forceUpdate || (cachedCapability == null) || shouldUpdateSystemCapabilitySubscription; + boolean shouldSendGetCapabilityRequest = shouldForceUpdate || (cachedCapability == null) || shouldUpdateSystemCapabilitySubscription; boolean shouldCallListenerWithCachedValue = (cachedCapability != null) && (scListener != null) && !shouldSendGetCapabilityRequest; if (shouldCallListenerWithCachedValue) { @@ -481,6 +495,7 @@ public class SystemCapabilityManager { /** Gets the capability object that corresponds to the supplied capability type by calling the listener immediately with the cached value, if available. If not available, the listener will retrieve a new value and return that when the head unit responds. * @param systemCapabilityType Type of capability desired * @param scListener callback to execute upon retrieving capability + * @deprecated use {@link #getCapability(SystemCapabilityType, OnSystemCapabilityListener, boolean)} instead. */ @Deprecated public void getCapability(final SystemCapabilityType systemCapabilityType, final OnSystemCapabilityListener scListener) { @@ -490,6 +505,7 @@ public class SystemCapabilityManager { /** Gets the capability object that corresponds to the supplied capability type by returning the currently cached value immediately if available. Otherwise returns a null object and works in the background to retrieve the capability for the next call * @param systemCapabilityType Type of capability desired * @return Desired capability if it is cached in the manager, otherwise returns null + * @deprecated use {@link #getCapability(SystemCapabilityType, OnSystemCapabilityListener, boolean)} instead. */ @Deprecated public Object getCapability(final SystemCapabilityType systemCapabilityType) { @@ -541,6 +557,15 @@ public class SystemCapabilityManager { * @param subscribe flag to subscribe to updates of the supplied capability type. True means subscribe; false means cancel subscription; null means don't change current subscription status. */ private void retrieveCapability(final SystemCapabilityType systemCapabilityType, final OnSystemCapabilityListener scListener, final Boolean subscribe) { + if (currentHMILevel != null && currentHMILevel.equals(HMILevel.HMI_NONE)) { + String message = String.format("Attempted to update type: %s in HMI level NONE, which is not allowed. " + + "Please wait until you are in HMI BACKGROUND, LIMITED, or FULL before attempting to update any SystemCapabilityType", systemCapabilityType); + DebugTool.logError(message); + if (scListener != null) { + scListener.onError(message); + } + return; + } if (!systemCapabilityType.isQueryable() || systemCapabilityType == SystemCapabilityType.DISPLAYS) { String message = "This systemCapabilityType cannot be queried for"; DebugTool.logError(message); @@ -554,9 +579,9 @@ public class SystemCapabilityManager { /* The subscription flag in the request should be set based on multiple variables: - - if subscribe is null (no change), shouldSubscribe = current subscription status, or false if the HU does not support subscriptions - - if subscribe is false, then shouldSubscribe = false - - if subscribe is true and the HU supports subscriptions, then shouldSubscribe = true + - if subscribe is null (no change), willSubscribe = current subscription status, or false if the HU does not support subscriptions + - if subscribe is false, then willSubscribe = false + - if subscribe is true and the HU supports subscriptions, then willSubscribe = true */ boolean shouldSubscribe = (subscribe != null) ? subscribe : isSubscribedToSystemCapability(systemCapabilityType); final boolean willSubscribe = shouldSubscribe && supportsSubscriptions(); diff --git a/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java b/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java index 3eb082670..f5d87e774 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java +++ b/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java @@ -208,7 +208,9 @@ public interface ISdl { * Get SystemCapability Object * @param systemCapabilityType a system capability type that should be retrieved * @return the system capability provided if available, null if not + * @deprecated use {@link #getCapability(SystemCapabilityType, OnSystemCapabilityListener, boolean)} instead. */ + @Deprecated Object getCapability(SystemCapabilityType systemCapabilityType); /** @@ -216,9 +218,20 @@ public interface ISdl { * @param systemCapabilityType a system capability type that should be retrieved * @param scListener listener that will be called when the system capability is retrieved. If already cached, it * will be called immediately + * @deprecated use {@link #getCapability(SystemCapabilityType, OnSystemCapabilityListener, boolean)} instead. */ + @Deprecated void getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener); + /** Gets the capability object that corresponds to the supplied capability type by returning the currently cached value immediately (or null) as well as calling the listener immediately with the cached value, if available. If not available, the listener will retrieve a new value and return that when the head unit responds. + * <strong>If capability is not cached, the method will return null and trigger the supplied listener when the capability becomes available</strong> + * @param systemCapabilityType type of capability desired + * @param scListener callback to execute upon retrieving capability + * @param forceUpdate flag to force getting a new fresh copy of the capability from the head unit even if it is cached + * @return desired capability if it is cached in the manager, otherwise returns a null object + */ + Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate); + /** * Get RegisterAppInterfaceResponse * @return the RegisterAppInterfaceResponse if available, null if not diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java index 342a8be8a..1cd163422 100644 --- a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -1136,13 +1136,27 @@ public class LifecycleManager extends BaseLifecycleManager { @Override public Object getCapability(SystemCapabilityType systemCapabilityType) { - return LifecycleManager.this.systemCapabilityManager.getCapability(systemCapabilityType); + if (LifecycleManager.this.systemCapabilityManager != null) { + return LifecycleManager.this.systemCapabilityManager.getCapability(systemCapabilityType); + } else { + return null; + } } @Override public void getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener) { - LifecycleManager.this.systemCapabilityManager.getCapability(systemCapabilityType,scListener); + if (LifecycleManager.this.systemCapabilityManager != null) { + LifecycleManager.this.systemCapabilityManager.getCapability(systemCapabilityType, scListener); + } + } + @Override + public Object getCapability(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener scListener, boolean forceUpdate) { + if (LifecycleManager.this.systemCapabilityManager != null) { + return LifecycleManager.this.systemCapabilityManager.getCapability(systemCapabilityType, scListener, forceUpdate); + } else { + return null; + } } @Override @@ -1152,18 +1166,27 @@ public class LifecycleManager extends BaseLifecycleManager { @Override public boolean isCapabilitySupported(SystemCapabilityType systemCapabilityType) { - return LifecycleManager.this.systemCapabilityManager.isCapabilitySupported(systemCapabilityType); + if (LifecycleManager.this.systemCapabilityManager != null) { + return LifecycleManager.this.systemCapabilityManager.isCapabilitySupported(systemCapabilityType); + } else { + return false; + } } @Override public void addOnSystemCapabilityListener(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener listener) { - LifecycleManager.this.systemCapabilityManager.addOnSystemCapabilityListener(systemCapabilityType,listener); - + if (LifecycleManager.this.systemCapabilityManager != null) { + LifecycleManager.this.systemCapabilityManager.addOnSystemCapabilityListener(systemCapabilityType, listener); + } } @Override public boolean removeOnSystemCapabilityListener(SystemCapabilityType systemCapabilityType, OnSystemCapabilityListener listener) { - return LifecycleManager.this.systemCapabilityManager.removeOnSystemCapabilityListener(systemCapabilityType,listener); + if (LifecycleManager.this.systemCapabilityManager != null) { + return LifecycleManager.this.systemCapabilityManager.removeOnSystemCapabilityListener(systemCapabilityType, listener); + } else { + return false; + } } @Override |
