summaryrefslogtreecommitdiff
path: root/android/sdl_android/src
diff options
context:
space:
mode:
Diffstat (limited to 'android/sdl_android/src')
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java4
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManagerTests.java105
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenDeviceIconManager.java214
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java36
4 files changed, 346 insertions, 13 deletions
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/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();
}