diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 4d337b6f316ab7f7f2deb217e74902bfd6301456..91295c7723338571fa4ac392f9b38f3d31867a8e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -24,6 +24,8 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 
+import androidx.annotation.NonNull;
+
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.event.Event;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
@@ -88,17 +90,19 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
                 InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
     }
 
-    public void updateKeyboardTheme() {
+    public void updateKeyboardTheme(@NonNull Context displayContext) {
         final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
-                mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */));
+                displayContext, KeyboardTheme.getKeyboardTheme(displayContext /* context */));
         if (themeUpdated && mKeyboardView != null) {
-            mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled));
+            mLatinIME.setInputView(
+                    onCreateInputView(displayContext, mIsHardwareAcceleratedDrawingEnabled));
         }
     }
 
     private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
             final KeyboardTheme keyboardTheme) {
-        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
+        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)
+                || !mThemeContext.getResources().equals(context.getResources())) {
             mKeyboardTheme = keyboardTheme;
             mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
             KeyboardLayoutSet.onKeyboardThemeChanged();
@@ -454,13 +458,14 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
         }
     }
 
-    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
+    public View onCreateInputView(@NonNull Context displayContext,
+            final boolean isHardwareAcceleratedDrawingEnabled) {
         if (mKeyboardView != null) {
             mKeyboardView.closing();
         }
 
         updateKeyboardThemeAndContextThemeWrapper(
-                mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */));
+                displayContext, KeyboardTheme.getKeyboardTheme(displayContext /* context */));
         mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                 R.layout.input_view, null);
         mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 31307bad1631951a47bf35ca33578e05c9496b6d..737bd0d3ece0d152741c63dea6660df3742f6b55 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
 import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE;
 import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;
@@ -107,6 +109,7 @@ import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 /**
  * Input method implementation for Qwerty'ish keyboard.
@@ -165,6 +168,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     // {@link #onEvaluateInputViewShown()}.
     private boolean mIsExecutingStartShowingInputView;
 
+    // Used for re-initialize keyboard layout after onConfigurationChange.
+    @Nullable private Context mDisplayContext;
+    private int mCurDisplayId = INVALID_DISPLAY;
+
     // Object for reacting to adding/removing a dictionary pack.
     private final BroadcastReceiver mDictionaryPackInstallReceiver =
             new DictionaryPackInstallBroadcastReceiver(this);
@@ -593,10 +600,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         DebugFlags.init(PreferenceManager.getDefaultSharedPreferences(this));
         RichInputMethodManager.init(this);
         mRichImm = RichInputMethodManager.getInstance();
-        KeyboardSwitcher.init(this);
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
         mStatsUtilsManager.onCreate(this /* context */, mDictionaryFacilitator);
+        final WindowManager wm = getSystemService(WindowManager.class);
+        mDisplayContext = createDisplayContext(wm.getDefaultDisplay());
+        mCurDisplayId = wm.getDefaultDisplay().getDisplayId();
+        KeyboardSwitcher.init(this);
         super.onCreate();
 
         mHandler.onCreate();
@@ -783,10 +793,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         super.onConfigurationChanged(conf);
     }
 
+    @Override
+    public void onInitializeInterface() {
+        // TODO (b/133825283): Non-activity components Resources / DisplayMetrics update when
+        //  moving to external display.
+        // An issue in Q that non-activity components Resources / DisplayMetrics in
+        // Context doesn't well updated when moving to external display.
+        // Currently we do a workaround is to check if IME is moving to new display, if so,
+        // create new display context and re-init keyboard layout with this context.
+        final WindowManager wm = getSystemService(WindowManager.class);
+        final int newDisplayId = wm.getDefaultDisplay().getDisplayId();
+        if (mCurDisplayId != newDisplayId) {
+            mCurDisplayId = newDisplayId;
+            mDisplayContext = createDisplayContext(wm.getDefaultDisplay());
+            mKeyboardSwitcher.updateKeyboardTheme(mDisplayContext);
+        }
+    }
+
     @Override
     public View onCreateInputView() {
         StatsUtils.onCreateInputView();
-        return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled);
+        return mKeyboardSwitcher.onCreateInputView(mDisplayContext,
+                mIsHardwareAcceleratedDrawingEnabled);
     }
 
     @Override
@@ -869,7 +897,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
         mRichImm.refreshSubtypeCaches();
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
-        switcher.updateKeyboardTheme();
+        switcher.updateKeyboardTheme(mDisplayContext);
         final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
         // If we are starting input in a different text field from before, we'll have to reload
         // settings, so currentSettingsValues can't be final.