1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.ui; 6 7import android.content.Context; 8import android.graphics.Bitmap; 9import android.graphics.Canvas; 10import android.graphics.Rect; 11import android.util.Log; 12import android.view.SurfaceView; 13import android.view.View; 14import android.view.ViewGroup; 15import android.view.inputmethod.InputMethodManager; 16 17/** 18 * Utility functions for common Android UI tasks. 19 * This class is not supposed to be instantiated. 20 */ 21public class UiUtils { 22 private static final String TAG = "UiUtils"; 23 24 /** 25 * Guards this class from being instantiated. 26 */ 27 private UiUtils() { 28 } 29 30 /** The minimum size of the bottom margin below the app to detect a keyboard. */ 31 private static final float KEYBOARD_DETECT_BOTTOM_THRESHOLD_DP = 100; 32 33 /** 34 * Shows the software keyboard if necessary. 35 * @param view The currently focused {@link View}, which would receive soft keyboard input. 36 */ 37 public static void showKeyboard(View view) { 38 InputMethodManager imm = 39 (InputMethodManager) view.getContext().getSystemService( 40 Context.INPUT_METHOD_SERVICE); 41 // Only shows soft keyboard if there isn't an open physical keyboard. 42 imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); 43 } 44 45 /** 46 * Hides the keyboard. 47 * @param view The {@link View} that is currently accepting input. 48 * @return Whether the keyboard was visible before. 49 */ 50 public static boolean hideKeyboard(View view) { 51 InputMethodManager imm = 52 (InputMethodManager) view.getContext().getSystemService( 53 Context.INPUT_METHOD_SERVICE); 54 return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 55 } 56 57 public static boolean isKeyboardShowing(Context context, View view) { 58 View rootView = view.getRootView(); 59 if (rootView == null) return false; 60 Rect appRect = new Rect(); 61 rootView.getWindowVisibleDisplayFrame(appRect); 62 final float screenHeight = context.getResources().getDisplayMetrics().heightPixels; 63 final float bottomMargin = Math.abs(appRect.bottom - screenHeight); 64 final float density = context.getResources().getDisplayMetrics().density; 65 return bottomMargin > KEYBOARD_DETECT_BOTTOM_THRESHOLD_DP * density; 66 } 67 68 /** 69 * Inserts a {@link View} into a {@link ViewGroup} after directly before a given {@View}. 70 * @param container The {@link View} to add newView to. 71 * @param newView The new {@link View} to add. 72 * @param existingView The {@link View} to insert the newView before. 73 * @return The index where newView was inserted, or -1 if it was not inserted. 74 */ 75 public static int insertBefore(ViewGroup container, View newView, View existingView) { 76 return insertView(container, newView, existingView, false); 77 } 78 79 /** 80 * Inserts a {@link View} into a {@link ViewGroup} after directly after a given {@View}. 81 * @param container The {@link View} to add newView to. 82 * @param newView The new {@link View} to add. 83 * @param existingView The {@link View} to insert the newView after. 84 * @return The index where newView was inserted, or -1 if it was not inserted. 85 */ 86 public static int insertAfter(ViewGroup container, View newView, View existingView) { 87 return insertView(container, newView, existingView, true); 88 } 89 90 private static int insertView( 91 ViewGroup container, View newView, View existingView, boolean after) { 92 // See if the view has already been added. 93 int index = container.indexOfChild(newView); 94 if (index >= 0) return index; 95 96 // Find the location of the existing view. 97 index = container.indexOfChild(existingView); 98 if (index < 0) return -1; 99 100 // Add the view. 101 if (after) index++; 102 container.addView(newView, index); 103 return index; 104 } 105 106 /** 107 * Generates a scaled screenshot of the given view. The maximum size of the screenshot is 108 * determined by maximumDimension. 109 * 110 * @param currentView The view to generate a screenshot of. 111 * @param maximumDimension The maximum width or height of the generated screenshot. The bitmap 112 * will be scaled to ensure the maximum width or height is equal to or 113 * less than this. Any value <= 0, will result in no scaling. 114 * @param bitmapConfig Bitmap config for the generated screenshot (ARGB_8888 or RGB_565). 115 * @return The screen bitmap of the view or null if a problem was encountered. 116 */ 117 public static Bitmap generateScaledScreenshot( 118 View currentView, int maximumDimension, Bitmap.Config bitmapConfig) { 119 Bitmap screenshot = null; 120 boolean drawingCacheEnabled = currentView.isDrawingCacheEnabled(); 121 try { 122 prepareViewHierarchyForScreenshot(currentView, true); 123 if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(true); 124 // Android has a maximum drawing cache size and if the drawing cache is bigger 125 // than that, getDrawingCache() returns null. 126 Bitmap originalBitmap = currentView.getDrawingCache(); 127 if (originalBitmap != null) { 128 double originalHeight = originalBitmap.getHeight(); 129 double originalWidth = originalBitmap.getWidth(); 130 int newWidth = (int) originalWidth; 131 int newHeight = (int) originalHeight; 132 if (maximumDimension > 0) { 133 double scale = maximumDimension / Math.max(originalWidth, originalHeight); 134 newWidth = (int) Math.round(originalWidth * scale); 135 newHeight = (int) Math.round(originalHeight * scale); 136 } 137 Bitmap scaledScreenshot = 138 Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true); 139 if (scaledScreenshot.getConfig() != bitmapConfig) { 140 screenshot = scaledScreenshot.copy(bitmapConfig, false); 141 scaledScreenshot.recycle(); 142 scaledScreenshot = null; 143 } else { 144 screenshot = scaledScreenshot; 145 } 146 } else if (currentView.getMeasuredHeight() > 0 && currentView.getMeasuredWidth() > 0) { 147 double originalHeight = currentView.getMeasuredHeight(); 148 double originalWidth = currentView.getMeasuredWidth(); 149 int newWidth = (int) originalWidth; 150 int newHeight = (int) originalHeight; 151 if (maximumDimension > 0) { 152 double scale = maximumDimension / Math.max(originalWidth, originalHeight); 153 newWidth = (int) Math.round(originalWidth * scale); 154 newHeight = (int) Math.round(originalHeight * scale); 155 } 156 Bitmap bitmap = Bitmap.createBitmap(newWidth, newHeight, bitmapConfig); 157 Canvas canvas = new Canvas(bitmap); 158 canvas.scale((float) (newWidth / originalWidth), 159 (float) (newHeight / originalHeight)); 160 currentView.draw(canvas); 161 screenshot = bitmap; 162 } 163 } catch (OutOfMemoryError e) { 164 Log.d(TAG, "Unable to capture screenshot and scale it down." + e.getMessage()); 165 } finally { 166 if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(false); 167 prepareViewHierarchyForScreenshot(currentView, false); 168 } 169 return screenshot; 170 } 171 172 private static void prepareViewHierarchyForScreenshot(View view, boolean takingScreenshot) { 173 if (view instanceof ViewGroup) { 174 ViewGroup viewGroup = (ViewGroup) view; 175 for (int i = 0; i < viewGroup.getChildCount(); i++) { 176 prepareViewHierarchyForScreenshot(viewGroup.getChildAt(i), takingScreenshot); 177 } 178 } else if (view instanceof SurfaceView) { 179 view.setWillNotDraw(!takingScreenshot); 180 } 181 } 182} 183