diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index c78be48a920a556a817897c6d7a2ae6c6dbd8e3f..ea81ce73c152d38c8339391f1d14be05cfd1b63a 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
 
     <uses-feature
         android:name="android.hardware.location.gps"
diff --git a/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.java b/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.java
index ecc0bd3ebc50a42f58176a47087d7908a03bf2ab..56072e9a1da84e675dcbcbcd35ba447488d6965c 100644
--- a/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.java
+++ b/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.java
@@ -25,6 +25,7 @@ public abstract class LinkedPage implements Parcelable {
     public abstract String id();
     public abstract String title();
     public abstract String icon();
+    public abstract String iconPath();
     public abstract String link();
 
     @AutoValue.Builder
@@ -32,6 +33,7 @@ public abstract class LinkedPage implements Parcelable {
         public abstract Builder id(String id);
         public abstract Builder title(String title);
         public abstract Builder icon(String icon);
+        public abstract Builder iconPath(String iconPath);
         public abstract Builder link(String link);
 
         public LinkedPage build() {
@@ -67,6 +69,7 @@ public abstract class LinkedPage implements Parcelable {
                 .id(id)
                 .title(title)
                 .icon(icon)
+                .iconPath(String.format("images/%s.png", icon))
                 .link(link)
                 .build();
     }
@@ -75,10 +78,12 @@ public abstract class LinkedPage implements Parcelable {
         if (jsonObject == null) {
             return null;
         }
+        String icon = jsonObject.optString("icon", null);
         return new AutoValue_LinkedPage.Builder()
                 .id(jsonObject.optString("id", null))
                 .title(jsonObject.optString("title", null))
-                .icon(jsonObject.optString("icon", null))
+                .icon(icon)
+                .iconPath(String.format("icon/%s", icon))
                 .link(jsonObject.optString("link", null))
                 .build();
     }
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java b/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java
index be7ded1b9f10152b9894c8c3c066c05363ec0842..f0188f8369e13b1bf02603abf7775695d3d6c5fa 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java
@@ -248,7 +248,7 @@ public class MainActivity extends AbstractBaseActivity implements
         }
 
         //  Create a new boolean and preference and set it to true
-        boolean isFirstStart = mPrefs.getBoolean("firstStart", true);
+        boolean isFirstStart = mPrefs.getBoolean(Constants.PREFERENCE_FIRST_START, true);
 
         SharedPreferences.Editor prefsEditor = mPrefs.edit();
         //  If the activity has never started before...
@@ -257,7 +257,7 @@ public class MainActivity extends AbstractBaseActivity implements
             final Intent i = new Intent(MainActivity.this, IntroActivity.class);
             startActivityForResult(i, INTRO_REQUEST_CODE);
 
-            prefsEditor.putBoolean("firstStart", false);
+            prefsEditor.putBoolean(Constants.PREFERENCE_FIRST_START, false);
         }
         OnUpdateBroadcastReceiver.updateComparableVersion(prefsEditor);
         prefsEditor.apply();
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java
index 1124115864bbbd31bac562555a935fd118153cee..bcae60cae2c5066976bdd70b3165b9528f81f6e8 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java
@@ -85,7 +85,7 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder
 
     public interface ItemClickListener {
         boolean onItemClicked(Widget item); // returns whether click was handled
-        void onItemLongClicked(Widget item);
+        boolean onItemLongClicked(Widget item);
     }
 
     private static final int TYPE_GENERICITEM = 0;
@@ -346,7 +346,7 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder
         ViewHolder holder = (ViewHolder) view.getTag();
         int position = holder.getAdapterPosition();
         if (position != RecyclerView.NO_POSITION) {
-            mItemClickListener.onItemLongClicked(mItems.get(position));
+            return mItemClickListener.onItemLongClicked(mItems.get(position));
         }
         return false;
     }
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java
index d2fdc17153ad06cdfb1e7ee5736e4224b537eca8..16793e77978a708a7c0ab5f2e5608cb8c9095773 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java
@@ -9,9 +9,19 @@
 
 package org.openhab.habdroid.ui;
 
+import android.annotation.SuppressLint;
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -19,11 +29,18 @@ import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import es.dmoral.toasty.Toasty;
 import org.openhab.habdroid.R;
+import org.openhab.habdroid.core.connection.Connection;
+import org.openhab.habdroid.core.connection.ConnectionFactory;
+import org.openhab.habdroid.core.connection.exception.ConnectionException;
 import org.openhab.habdroid.model.Item;
 import org.openhab.habdroid.model.LabeledValue;
 import org.openhab.habdroid.model.LinkedPage;
@@ -31,6 +48,7 @@ import org.openhab.habdroid.model.ParsedState;
 import org.openhab.habdroid.model.Widget;
 import org.openhab.habdroid.ui.widget.RecyclerViewSwipeRefreshLayout;
 import org.openhab.habdroid.util.CacheManager;
+import org.openhab.habdroid.util.Constants;
 import org.openhab.habdroid.util.Util;
 
 import java.util.ArrayList;
@@ -108,7 +126,7 @@ public class WidgetListFragment extends Fragment
     }
 
     @Override
-    public void onItemLongClicked(final Widget widget) {
+    public boolean onItemLongClicked(final Widget widget) {
         ArrayList<String> labels = new ArrayList<>();
         ArrayList<String> commands = new ArrayList<>();
 
@@ -184,6 +202,9 @@ public class WidgetListFragment extends Fragment
 
         if (widget.linkedPage() != null) {
             labels.add(getString(R.string.nfc_action_to_sitemap_page));
+            if (ShortcutManagerCompat.isRequestPinShortcutSupported(getContext())) {
+                labels.add(getString(R.string.home_shortcut_pin_to_home));
+            }
         }
 
         if (!labels.isEmpty()) {
@@ -191,19 +212,22 @@ public class WidgetListFragment extends Fragment
             new AlertDialog.Builder(getActivity())
                     .setTitle(R.string.nfc_dialog_title)
                     .setItems(labelArray, (dialog, which) -> {
-                        final Intent writeTagIntent;
                         if (which < commands.size()) {
-                            writeTagIntent = WriteTagActivity.createItemUpdateIntent(getActivity(),
-                                    widget.item().name(), commands.get(which),
-                                    labels.get(which), widget.item().label());
+                            startActivity(WriteTagActivity.createItemUpdateIntent(
+                                    getActivity(), widget.item().name(), commands.get(which),
+                                    labels.get(which), widget.item().label()));
+                        } else if (which == commands.size()) {
+                            startActivity(WriteTagActivity.createSitemapNavigationIntent(
+                                    getActivity(), widget.linkedPage().link()));
                         } else {
-                            writeTagIntent = WriteTagActivity.createSitemapNavigationIntent(
-                                    getActivity(), widget.linkedPage().link());
+                            createShortcut(widget.linkedPage());
                         }
-                        startActivityForResult(writeTagIntent, 0);
                     })
                     .show();
+
+            return true;
         }
+        return false;
     }
 
     @Override
@@ -251,6 +275,106 @@ public class WidgetListFragment extends Fragment
         }
     }
 
+    @SuppressLint("StaticFieldLeak")
+    private void createShortcut(LinkedPage linkedPage) {
+        String iconFormat = PreferenceManager.getDefaultSharedPreferences(getContext())
+                .getString(Constants.PREFERENCE_ICON_FORMAT, "png");
+        new AsyncTask<Void, Void, IconCompat>() {
+
+            @Override
+            protected IconCompat doInBackground(Void... voids) {
+                Context context = getContext();
+                String url = null;
+                if (!TextUtils.isEmpty(linkedPage.iconPath())) {
+                    url = new Uri.Builder()
+                            .appendEncodedPath(linkedPage.iconPath())
+                            .appendQueryParameter("format", iconFormat)
+                            .toString();
+                }
+                IconCompat icon = null;
+                Connection connection = null;
+                try {
+                    connection = ConnectionFactory.getUsableConnection();
+                } catch (ConnectionException e) {
+                    // ignored
+                }
+
+                if (context == null || connection == null) {
+                    return null;
+                }
+
+                if (url != null) {
+                    /**
+                     *  Icon size is defined in {@link AdaptiveIconDrawable}. Foreground size of
+                     *  46dp instead of 72dp adds enough border to the icon.
+                     *  46dp foreground + 2 * 31dp border = 108dp
+                     **/
+                    int foregroundSize = (int) Util.convertDpToPixel(46, context);
+                    Bitmap bitmap = connection.getSyncHttpClient().get(url)
+                            .asBitmap(foregroundSize).response;
+                    if (bitmap != null) {
+                        bitmap = addBackgroundAndBorder(bitmap,
+                                (int) Util.convertDpToPixel(31, context));
+                        icon = IconCompat.createWithAdaptiveBitmap(bitmap);
+                    }
+                }
+
+                if (icon == null) {
+                    // Fall back to openHAB icon
+                    icon = IconCompat.createWithResource(context, R.mipmap.icon);
+                }
+
+                return icon;
+            }
+
+            @Override
+            protected void onPostExecute(IconCompat icon) {
+                Context context = getContext();
+                if (icon == null || context == null) {
+                    return;
+                }
+
+                Uri sitemapUri = Uri.parse(linkedPage.link());
+                String shortSitemapUri = sitemapUri.getPath().substring(14);
+
+                Intent startIntent = new Intent(context, MainActivity.class);
+                startIntent.setAction(MainActivity.ACTION_SITEMAP_SELECTED);
+                startIntent.putExtra(MainActivity.EXTRA_SITEMAP_URL, shortSitemapUri);
+                startActivity(startIntent);
+
+                String name = linkedPage.title();
+                if (TextUtils.isEmpty(name)) {
+                    name = getString(R.string.app_name);
+                }
+
+                ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context,
+                        shortSitemapUri + '-' + System.currentTimeMillis())
+                        .setShortLabel(name)
+                        .setIcon(icon)
+                        .setIntent(startIntent)
+                        .build();
+
+                if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
+                    Toasty.success(context,R.string.home_shortcut_success_pinning).show();
+                } else {
+                    Toasty.error(context,R.string.home_shortcut_error_pinning).show();
+                }
+            }
+        }.execute();
+    }
+
+    /**
+     * @author https://stackoverflow.com/a/15525394
+     */
+    private Bitmap addBackgroundAndBorder(Bitmap bitmap, int borderSize) {
+        Bitmap bitmapWithBackground = Bitmap.createBitmap(bitmap.getWidth() + borderSize * 2,
+                bitmap.getHeight() + borderSize * 2, bitmap.getConfig());
+        Canvas canvas = new Canvas(bitmapWithBackground);
+        canvas.drawColor(Color.WHITE);
+        canvas.drawBitmap(bitmap, borderSize, borderSize, null);
+        return bitmapWithBackground;
+    }
+
     public static WidgetListFragment withPage(String pageUrl, String pageTitle) {
         WidgetListFragment fragment = new WidgetListFragment();
         Bundle args = new Bundle();
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/activity/ContentController.java b/mobile/src/main/java/org/openhab/habdroid/ui/activity/ContentController.java
index 71dc4ca126f1835937e684ceea31ba5276790c38..4d6ed19991b7bb9e51c295927629d1ee5c5b2f9b 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/activity/ContentController.java
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/activity/ContentController.java
@@ -431,7 +431,7 @@ public abstract class ContentController implements PageConnectionHolderFragment.
     @Override
     public String getIconFormat() {
         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity);
-        return prefs.getString("iconFormatType","PNG");
+        return prefs.getString(Constants.PREFERENCE_ICON_FORMAT, "PNG");
     }
 
     @Override
diff --git a/mobile/src/main/java/org/openhab/habdroid/util/AsyncHttpClient.java b/mobile/src/main/java/org/openhab/habdroid/util/AsyncHttpClient.java
index e52607872ff14c3357fb87604f85c42413f80c8d..08ab6d5e7709904003046806d19a81e551812e53 100644
--- a/mobile/src/main/java/org/openhab/habdroid/util/AsyncHttpClient.java
+++ b/mobile/src/main/java/org/openhab/habdroid/util/AsyncHttpClient.java
@@ -9,28 +9,20 @@
 
 package org.openhab.habdroid.util;
 
-import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.DisplayMetrics;
 import androidx.annotation.NonNull;
 
-import com.caverock.androidsvg.SVG;
-import com.caverock.androidsvg.SVGParseException;
 import okhttp3.Call;
 import okhttp3.Callback;
 import okhttp3.Headers;
-import okhttp3.MediaType;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.Response;
 import okhttp3.ResponseBody;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Map;
 
 public class AsyncHttpClient extends HttpClient {
@@ -57,74 +49,7 @@ public class AsyncHttpClient extends HttpClient {
 
         @Override
         public Bitmap convertBodyInBackground(ResponseBody body) throws IOException {
-            MediaType contentType = body.contentType();
-            boolean isSvg = contentType != null
-                    && contentType.type().equals("image")
-                    && contentType.subtype().contains("svg");
-            InputStream is = body.byteStream();
-            if (isSvg) {
-                try {
-                    return getBitmapFromSvgInputstream(Resources.getSystem(), is, mSize);
-                } catch (SVGParseException e) {
-                    throw new IOException("SVG decoding failed", e);
-                }
-            } else {
-                Bitmap bitmap = BitmapFactory.decodeStream(is);
-                if (bitmap != null) {
-                    return bitmap;
-                }
-                throw new IOException("Bitmap decoding failed");
-            }
-        }
-
-        private static Bitmap getBitmapFromSvgInputstream(Resources res, InputStream is, int size)
-                throws SVGParseException {
-            SVG svg = SVG.getFromInputStream(is);
-            svg.setRenderDPI(DisplayMetrics.DENSITY_DEFAULT);
-            Float density = res.getDisplayMetrics().density;
-            svg.setDocumentHeight("100%");
-            svg.setDocumentWidth("100%");
-            int docWidth = (int) (svg.getDocumentWidth() * density);
-            int docHeight = (int) (svg.getDocumentHeight() * density);
-            if (docWidth < 0 || docHeight < 0) {
-                float aspectRatio = svg.getDocumentAspectRatio();
-                if (aspectRatio > 0) {
-                    float heightForAspect = (float) size / aspectRatio;
-                    float widthForAspect = (float) size * aspectRatio;
-                    if (widthForAspect < heightForAspect) {
-                        docWidth = Math.round(widthForAspect);
-                        docHeight = size;
-                    } else {
-                        docWidth = size;
-                        docHeight = Math.round(heightForAspect);
-                    }
-                } else {
-                    docWidth = size;
-                    docHeight = size;
-                }
-
-                // we didn't take density into account anymore when calculating docWidth
-                // and docHeight, so don't scale with it and just let the renderer
-                // figure out the scaling
-                density = null;
-            }
-
-            if (docWidth != size || docHeight != size) {
-                float scaleWidth = (float) size / docWidth;
-                float scaleHeigth = (float) size / docHeight;
-                density = (scaleWidth + scaleHeigth) / 2;
-
-                docWidth = size;
-                docHeight = size;
-            }
-
-            Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(bitmap);
-            if (density != null) {
-                canvas.scale(density, density);
-            }
-            svg.renderToCanvas(canvas);
-            return bitmap;
+            return getBitmapFromResponseBody(body, mSize);
         }
     }
 
diff --git a/mobile/src/main/java/org/openhab/habdroid/util/HttpClient.java b/mobile/src/main/java/org/openhab/habdroid/util/HttpClient.java
index aaa8c1123117162ebda9b69d9a79345aeff7224d..f5cbd682122e95a50ff37adc5c5ed4fd4aac46da 100644
--- a/mobile/src/main/java/org/openhab/habdroid/util/HttpClient.java
+++ b/mobile/src/main/java/org/openhab/habdroid/util/HttpClient.java
@@ -9,8 +9,15 @@
 
 package org.openhab.habdroid.util;
 
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.util.DisplayMetrics;
 import androidx.annotation.VisibleForTesting;
 
+import com.caverock.androidsvg.SVG;
+import com.caverock.androidsvg.SVGParseException;
 import com.here.oksse.OkSse;
 import com.here.oksse.ServerSentEvent;
 import okhttp3.CacheControl;
@@ -21,7 +28,10 @@ import okhttp3.MediaType;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.RequestBody;
+import okhttp3.ResponseBody;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -111,4 +121,76 @@ public abstract class HttpClient {
         }
         return builder;
     }
+
+    protected static Bitmap getBitmapFromResponseBody(ResponseBody body, int size)
+            throws IOException {
+        MediaType contentType = body.contentType();
+        boolean isSvg = contentType != null
+                && contentType.type().equals("image")
+                && contentType.subtype().contains("svg");
+        InputStream is = body.byteStream();
+        if (isSvg) {
+            try {
+                return getBitmapFromSvgInputstream(Resources.getSystem(), is, size);
+            } catch (SVGParseException e) {
+                throw new IOException("SVG decoding failed", e);
+            }
+        } else {
+            Bitmap bitmap = BitmapFactory.decodeStream(is);
+            if (bitmap != null) {
+                return Bitmap.createScaledBitmap(bitmap, size, size, false);
+            }
+            throw new IOException("Bitmap decoding failed");
+        }
+    }
+
+    private static Bitmap getBitmapFromSvgInputstream(Resources res, InputStream is, int size)
+            throws SVGParseException {
+        SVG svg = SVG.getFromInputStream(is);
+        svg.setRenderDPI(DisplayMetrics.DENSITY_DEFAULT);
+        Float density = res.getDisplayMetrics().density;
+        svg.setDocumentHeight("100%");
+        svg.setDocumentWidth("100%");
+        int docWidth = (int) (svg.getDocumentWidth() * density);
+        int docHeight = (int) (svg.getDocumentHeight() * density);
+        if (docWidth < 0 || docHeight < 0) {
+            float aspectRatio = svg.getDocumentAspectRatio();
+            if (aspectRatio > 0) {
+                float heightForAspect = (float) size / aspectRatio;
+                float widthForAspect = (float) size * aspectRatio;
+                if (widthForAspect < heightForAspect) {
+                    docWidth = Math.round(widthForAspect);
+                    docHeight = size;
+                } else {
+                    docWidth = size;
+                    docHeight = Math.round(heightForAspect);
+                }
+            } else {
+                docWidth = size;
+                docHeight = size;
+            }
+
+            // we didn't take density into account anymore when calculating docWidth
+            // and docHeight, so don't scale with it and just let the renderer
+            // figure out the scaling
+            density = null;
+        }
+
+        if (docWidth != size || docHeight != size) {
+            float scaleWidth = (float) size / docWidth;
+            float scaleHeigth = (float) size / docHeight;
+            density = (scaleWidth + scaleHeigth) / 2;
+
+            docWidth = size;
+            docHeight = size;
+        }
+
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        if (density != null) {
+            canvas.scale(density, density);
+        }
+        svg.renderToCanvas(canvas);
+        return bitmap;
+    }
 }
diff --git a/mobile/src/main/java/org/openhab/habdroid/util/SyncHttpClient.java b/mobile/src/main/java/org/openhab/habdroid/util/SyncHttpClient.java
index 61834a39b9c10a208cc871068ba3f3b6dd203512..41e0e5b64ba5a2567fb9273ec63065f37a78dfda 100644
--- a/mobile/src/main/java/org/openhab/habdroid/util/SyncHttpClient.java
+++ b/mobile/src/main/java/org/openhab/habdroid/util/SyncHttpClient.java
@@ -9,6 +9,8 @@
 
 package org.openhab.habdroid.util;
 
+import android.graphics.Bitmap;
+
 import okhttp3.Call;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
@@ -61,6 +63,10 @@ public class SyncHttpClient extends HttpClient {
             return new HttpTextResult(this);
         }
 
+        public HttpBitmapResult asBitmap(int sizeInPixels) {
+            return new HttpBitmapResult(this, sizeInPixels);
+        }
+
         public HttpStatusResult asStatus() {
             return new HttpStatusResult(this);
         }
@@ -112,6 +118,33 @@ public class SyncHttpClient extends HttpClient {
         }
     }
 
+    public static class HttpBitmapResult {
+        public final Request request;
+        public final Bitmap response;
+        public final Throwable error;
+        public final int statusCode;
+
+        HttpBitmapResult(HttpResult result, int size) {
+            this.request = result.request;
+            this.statusCode = result.statusCode;
+            if (result.response == null) {
+                this.response = null;
+                this.error = result.error;
+            } else {
+                Bitmap response = null;
+                Throwable error = result.error;
+                try {
+                    response = getBitmapFromResponseBody(result.response, size);
+                } catch (IOException e) {
+                    error = e;
+                }
+                this.response = response;
+                this.error = error;
+            }
+            result.close();
+        }
+    }
+
     public SyncHttpClient(OkHttpClient client, String baseUrl, String username, String password) {
         super(client, baseUrl, username, password);
     }
diff --git a/mobile/src/main/java/org/openhab/habdroid/util/Util.java b/mobile/src/main/java/org/openhab/habdroid/util/Util.java
index 49c5d55f89e089d63a2e99a6097a5dec626db300..6443810ad553dd930086f0d3695797e2a223ee3a 100644
--- a/mobile/src/main/java/org/openhab/habdroid/util/Util.java
+++ b/mobile/src/main/java/org/openhab/habdroid/util/Util.java
@@ -22,6 +22,7 @@ import android.os.Looper;
 import android.os.Message;
 import android.preference.PreferenceManager;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.webkit.WebChromeClient;
@@ -399,4 +400,18 @@ public class Util {
                 R.drawable.ic_openhab_appicon_24dp, R.color.openhab_orange, Toasty.LENGTH_SHORT,
                 true, true).show());
     }
+
+    /**
+     * This method converts dp unit to equivalent pixels, depending on device density.
+     *
+     * @param dp A value in dp (density independent pixels) unit. Which we need to convert into
+     *          pixels
+     * @param context Context to get resources and device specific display metrics
+     * @return A float value to represent px equivalent to dp depending on device density
+     * @author https://stackoverflow.com/a/9563438
+     */
+    public static float convertDpToPixel(float dp, Context context) {
+        return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
+                / DisplayMetrics.DENSITY_DEFAULT);
+    }
 }
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index bfdf3e1a5c27d0b4a1172647b6dea06e5c552efd..c68fa3cf977783f3ea5a1c26434ece5f9d652deb 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -255,6 +255,11 @@
     <string name="clear_log">Clear log</string>
     <string name="empty_log">Log is empty</string>
 
+    <!-- Home screen shortcuts -->
+    <string name="home_shortcut_pin_to_home">Pin to home</string>
+    <string name="home_shortcut_error_pinning">Error pinning to home</string>
+    <string name="home_shortcut_success_pinning">Pinned to home</string>
+
     <!-- Content description for images -->
     <string name="content_description_open_roller_shutter">Open roller shutter</string>
     <string name="content_description_stop_roller_shutter">Stop roller shutter</string>