Created
January 2, 2016 04:12
-
-
Save jaredsburrows/8611f160db41b2336ee9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2013 The Chromium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
package org.chromium.ui; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.Canvas; | |
import android.graphics.Rect; | |
import android.util.Log; | |
import android.view.SurfaceView; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.view.inputmethod.InputMethodManager; | |
/** | |
* Utility functions for common Android UI tasks. | |
* This class is not supposed to be instantiated. | |
*/ | |
public class UiUtils { | |
private static final String TAG = "UiUtils"; | |
/** | |
* Guards this class from being instantiated. | |
*/ | |
private UiUtils() { | |
} | |
/** The minimum size of the bottom margin below the app to detect a keyboard. */ | |
private static final float KEYBOARD_DETECT_BOTTOM_THRESHOLD_DP = 100; | |
/** A delegate that allows disabling keyboard visibility detection. */ | |
private static KeyboardShowingDelegate sKeyboardShowingDelegate; | |
/** | |
* A delegate that can be implemented to override whether or not keyboard detection will be | |
* used. | |
*/ | |
public interface KeyboardShowingDelegate { | |
/** | |
* Will be called to determine whether or not to detect if the keyboard is visible. | |
* @param context A {@link Context} instance. | |
* @param view A {@link View}. | |
* @return Whether or not the keyboard check should be disabled. | |
*/ | |
boolean disableKeyboardCheck(Context context, View view); | |
} | |
/** | |
* Allows setting a delegate to override the default software keyboard visibility detection. | |
* @param delegate A {@link KeyboardShowingDelegate} instance. | |
*/ | |
public static void setKeyboardShowingDelegate(KeyboardShowingDelegate delegate) { | |
sKeyboardShowingDelegate = delegate; | |
} | |
/** | |
* Shows the software keyboard if necessary. | |
* @param view The currently focused {@link View}, which would receive soft keyboard input. | |
*/ | |
public static void showKeyboard(View view) { | |
InputMethodManager imm = | |
(InputMethodManager) view.getContext().getSystemService( | |
Context.INPUT_METHOD_SERVICE); | |
// Only shows soft keyboard if there isn't an open physical keyboard. | |
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); | |
} | |
/** | |
* Hides the keyboard. | |
* @param view The {@link View} that is currently accepting input. | |
* @return Whether the keyboard was visible before. | |
*/ | |
public static boolean hideKeyboard(View view) { | |
InputMethodManager imm = | |
(InputMethodManager) view.getContext().getSystemService( | |
Context.INPUT_METHOD_SERVICE); | |
return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); | |
} | |
/** | |
* Detects whether or not the keyboard is showing. This is a best guess as there is no | |
* standardized/foolproof way to do this. | |
* @param context A {@link Context} instance. | |
* @param view A {@link View}. | |
* @return Whether or not the software keyboard is visible and taking up screen space. | |
*/ | |
public static boolean isKeyboardShowing(Context context, View view) { | |
if (sKeyboardShowingDelegate != null | |
&& sKeyboardShowingDelegate.disableKeyboardCheck(context, view)) { | |
return false; | |
} | |
View rootView = view.getRootView(); | |
if (rootView == null) return false; | |
Rect appRect = new Rect(); | |
rootView.getWindowVisibleDisplayFrame(appRect); | |
final float density = context.getResources().getDisplayMetrics().density; | |
final float bottomMarginDp = Math.abs(rootView.getHeight() - appRect.height()) / density; | |
return bottomMarginDp > KEYBOARD_DETECT_BOTTOM_THRESHOLD_DP; | |
} | |
/** | |
* Inserts a {@link View} into a {@link ViewGroup} after directly before a given {@View}. | |
* @param container The {@link View} to add newView to. | |
* @param newView The new {@link View} to add. | |
* @param existingView The {@link View} to insert the newView before. | |
* @return The index where newView was inserted, or -1 if it was not inserted. | |
*/ | |
public static int insertBefore(ViewGroup container, View newView, View existingView) { | |
return insertView(container, newView, existingView, false); | |
} | |
/** | |
* Inserts a {@link View} into a {@link ViewGroup} after directly after a given {@View}. | |
* @param container The {@link View} to add newView to. | |
* @param newView The new {@link View} to add. | |
* @param existingView The {@link View} to insert the newView after. | |
* @return The index where newView was inserted, or -1 if it was not inserted. | |
*/ | |
public static int insertAfter(ViewGroup container, View newView, View existingView) { | |
return insertView(container, newView, existingView, true); | |
} | |
private static int insertView( | |
ViewGroup container, View newView, View existingView, boolean after) { | |
// See if the view has already been added. | |
int index = container.indexOfChild(newView); | |
if (index >= 0) return index; | |
// Find the location of the existing view. | |
index = container.indexOfChild(existingView); | |
if (index < 0) return -1; | |
// Add the view. | |
if (after) index++; | |
container.addView(newView, index); | |
return index; | |
} | |
/** | |
* Generates a scaled screenshot of the given view. The maximum size of the screenshot is | |
* determined by maximumDimension. | |
* | |
* @param currentView The view to generate a screenshot of. | |
* @param maximumDimension The maximum width or height of the generated screenshot. The bitmap | |
* will be scaled to ensure the maximum width or height is equal to or | |
* less than this. Any value <= 0, will result in no scaling. | |
* @param bitmapConfig Bitmap config for the generated screenshot (ARGB_8888 or RGB_565). | |
* @return The screen bitmap of the view or null if a problem was encountered. | |
*/ | |
public static Bitmap generateScaledScreenshot( | |
View currentView, int maximumDimension, Bitmap.Config bitmapConfig) { | |
Bitmap screenshot = null; | |
boolean drawingCacheEnabled = currentView.isDrawingCacheEnabled(); | |
try { | |
prepareViewHierarchyForScreenshot(currentView, true); | |
if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(true); | |
// Android has a maximum drawing cache size and if the drawing cache is bigger | |
// than that, getDrawingCache() returns null. | |
Bitmap originalBitmap = currentView.getDrawingCache(); | |
if (originalBitmap != null) { | |
double originalHeight = originalBitmap.getHeight(); | |
double originalWidth = originalBitmap.getWidth(); | |
int newWidth = (int) originalWidth; | |
int newHeight = (int) originalHeight; | |
if (maximumDimension > 0) { | |
double scale = maximumDimension / Math.max(originalWidth, originalHeight); | |
newWidth = (int) Math.round(originalWidth * scale); | |
newHeight = (int) Math.round(originalHeight * scale); | |
} | |
Bitmap scaledScreenshot = | |
Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true); | |
if (scaledScreenshot.getConfig() != bitmapConfig) { | |
screenshot = scaledScreenshot.copy(bitmapConfig, false); | |
scaledScreenshot.recycle(); | |
scaledScreenshot = null; | |
} else { | |
screenshot = scaledScreenshot; | |
} | |
} else if (currentView.getMeasuredHeight() > 0 && currentView.getMeasuredWidth() > 0) { | |
double originalHeight = currentView.getMeasuredHeight(); | |
double originalWidth = currentView.getMeasuredWidth(); | |
int newWidth = (int) originalWidth; | |
int newHeight = (int) originalHeight; | |
if (maximumDimension > 0) { | |
double scale = maximumDimension / Math.max(originalWidth, originalHeight); | |
newWidth = (int) Math.round(originalWidth * scale); | |
newHeight = (int) Math.round(originalHeight * scale); | |
} | |
Bitmap bitmap = Bitmap.createBitmap(newWidth, newHeight, bitmapConfig); | |
Canvas canvas = new Canvas(bitmap); | |
canvas.scale((float) (newWidth / originalWidth), | |
(float) (newHeight / originalHeight)); | |
currentView.draw(canvas); | |
screenshot = bitmap; | |
} | |
} catch (OutOfMemoryError e) { | |
Log.d(TAG, "Unable to capture screenshot and scale it down." + e.getMessage()); | |
} finally { | |
if (!drawingCacheEnabled) currentView.setDrawingCacheEnabled(false); | |
prepareViewHierarchyForScreenshot(currentView, false); | |
} | |
return screenshot; | |
} | |
private static void prepareViewHierarchyForScreenshot(View view, boolean takingScreenshot) { | |
if (view instanceof ViewGroup) { | |
ViewGroup viewGroup = (ViewGroup) view; | |
for (int i = 0; i < viewGroup.getChildCount(); i++) { | |
prepareViewHierarchyForScreenshot(viewGroup.getChildAt(i), takingScreenshot); | |
} | |
} else if (view instanceof SurfaceView) { | |
view.setWillNotDraw(!takingScreenshot); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://chromium.googlesource.com/chromium/src.git/+/39.0.2164.0/ui/android/java/src/org/chromium/ui/UiUtils.java