package org.commcare.views;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.v4.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.commcare.suite.model.Action;
import org.commcare.suite.model.DisplayData;
import org.commcare.utils.MediaUtil;
import org.javarosa.core.services.locale.Localizer;
import java.util.ArrayList;
/**
* Utilities for converting CommCare UI display details into Android objects
*
* @author ctsims
*/
public final class ViewUtil {
public static void addActionToMenu(Context context, Action action, Menu menu, int menuId,
int menuGroupId) {
DisplayData display = action.getDisplay().evaluate();
MenuItem item = menu.add(menuGroupId, menuId, menuId,
Localizer.clearArguments(display.getName()).trim());
if (action.hasActionBarIcon() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
Bitmap b = MediaUtil.inflateDisplayImage(context, action.getActionBarIconReference());
item.setIcon(new BitmapDrawable(context.getResources(), b));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else if (display.getImageURI() != null) {
Bitmap b = MediaUtil.inflateDisplayImage(context, display.getImageURI());
if (b != null) {
item.setIcon(new BitmapDrawable(context.getResources(), b));
}
}
}
public static void addItemToActionBar(Menu menu, int menuId, int menuGroupId, String title,
@DrawableRes int drawableResource) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
MenuItem item = menu.add(menuGroupId, menuId, menuId, title);
item.setIcon(drawableResource);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
}
public static void hideVirtualKeyboard(Activity activity) {
InputMethodManager inputManager = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
View focus = activity.getCurrentFocus();
if (focus != null) {
inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}
/**
* Sets the background on a view to the provided drawable while retaining the padding
* of the original view (regardless of whether the provided drawable has its own padding)
*
* @param v The view whose background will be updated
* @param background A background drawable (can be null to clear the background)
*/
public static void setBackgroundRetainPadding(View v, Drawable background) {
//Need to transplant the padding due to background affecting it
int[] padding = {v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom()};
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
v.setBackground(background);
} else {
v.setBackgroundDrawable(background);
}
v.setPadding(padding[0], padding[1], padding[2], padding[3]);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static int getColorDrawableColor(ColorDrawable drawable) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
drawable.draw(canvas);
int pix = bitmap.getPixel(0, 0);
bitmap.recycle();
return pix;
} else {
return drawable.getColor();
}
}
/**
* Determine width of each child view, based on mHints, the suite's size hints.
* mHints contains a width hint for each child view, each one of
* - A string like "50%", requesting the field take up 50% of the row
* - A string like "200", requesting the field take up 200 pixels
* - Null, not specifying a width for the field
* This function will parcel out requested widths and divide remaining space among unspecified columns.
*
* @param fullSize Width, in pixels, of the containing row.
* @return Array of integers, each corresponding to a child view,
* representing the desired width, in pixels, of that view.
*/
public static int[] calculateColumnWidths(ArrayList<String> hints, int fullSize) {
// Convert any percentages to pixels. Percentage columns are treated
// as percentage of the entire screen width.
int[] widths = new int[hints.size()];
parseWidths(hints, widths, fullSize);
Pair<Integer, Integer> constraints = buildConstraints(widths);
int claimedSpace = constraints.first;
int indeterminateColumns = constraints.second;
if (widthReadjustmentNeeded(fullSize, claimedSpace, indeterminateColumns)) {
readjustWidths(widths, fullSize, claimedSpace, indeterminateColumns);
} else if (indeterminateColumns > 0) {
divideIndeterminateSpace(widths, fullSize, claimedSpace, indeterminateColumns);
}
return widths;
}
private static void parseWidths(ArrayList<String> hints, int[] widths, int fullSize) {
int hintIndex = 0;
for (String hint : hints) {
if (hint == null) {
widths[hintIndex] = -1;
} else if (hint.contains("%")) {
String percentString = hint.substring(0, hint.indexOf("%"));
widths[hintIndex] = fullSize * Integer.parseInt(percentString) / 100;
} else {
widths[hintIndex] = Integer.parseInt(hint);
}
hintIndex++;
}
}
private static Pair<Integer, Integer> buildConstraints(int[] widths) {
int claimedSpace = 0;
int indeterminateColumns = 0;
for (int width : widths) {
if (width != -1) {
claimedSpace += width;
} else {
indeterminateColumns++;
}
}
return new Pair<>(claimedSpace, indeterminateColumns);
}
/**
* Either more space has been claimed than the screen has room for, or the
* full width isn't spoken for and there are no indeterminate columns.
*/
private static boolean widthReadjustmentNeeded(int fullSize, int claimedSpace,
int indeterminateColumns) {
return (fullSize < claimedSpace + indeterminateColumns)
|| (fullSize > claimedSpace && indeterminateColumns == 0);
}
private static void readjustWidths(int[] widths, int fullSize,
int claimedSpace,
int indeterminateColumns) {
claimedSpace += indeterminateColumns;
for (int i = 0; i < widths.length; i++) {
if (widths[i] == -1) {
// Assign indeterminate columns a real width.
// It's arbitrary and tiny, but this is going to look terrible regardless.
widths[i] = 1;
} else {
// Shrink or expand columns proportionally
widths[i] = fullSize * widths[i] / claimedSpace;
}
}
}
private static void divideIndeterminateSpace(int[] widths, int fullSize,
int claimedSpace,
int indeterminateColumns) {
int defaultWidth = (fullSize - claimedSpace) / indeterminateColumns;
for (int i = 0; i < widths.length; i++) {
if (widths[i] == -1) {
widths[i] = defaultWidth;
}
}
}
}