package org.jetbrains.android.inspections.lint; import com.android.resources.Density; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlTagValue; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Eugene.Kudelevsky */ class ConvertToDpQuickFix implements AndroidLintQuickFix { private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.ConvertToDpQuickFix"); private static final Pattern PX_ATTR_VALUE_PATTERN = Pattern.compile("(\\d+)px"); private static int ourPrevDpi = Density.DEFAULT_DENSITY; @Override public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context) { if (context instanceof AndroidQuickfixContexts.BatchContext) { return; } final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); final List<Density> densities = new ArrayList<Density>(); for (Density density : Density.values()) { if (density.getDpiValue() > 0) { densities.add(density); } } final String[] densityPresentableNames = new String[densities.size()]; String defaultValue = null; String initialValue = null; for (int i = 0; i < densities.size(); i++) { final Density density = densities.get(i); densityPresentableNames[i] = getLabelForDensity(density); final int dpi = density.getDpiValue(); if (dpi == 0) { continue; } if (dpi == ourPrevDpi) { initialValue = densityPresentableNames[i]; } else if (dpi == Density.DEFAULT_DENSITY) { defaultValue = densityPresentableNames[i]; } } if (initialValue == null) { initialValue = defaultValue; } if (initialValue == null) { return; } final int dpi; if (ApplicationManager.getApplication().isUnitTestMode()) { dpi = Density.DEFAULT_DENSITY; } else { final int selectedIndex = Messages .showChooseDialog("What is the screen density the current px value works with?", "Choose density", densityPresentableNames, initialValue, null); if (selectedIndex < 0) { return; } dpi = densities.get(selectedIndex).getDpiValue(); } //noinspection AssignmentToStaticFieldFromInstanceMethod ourPrevDpi = dpi; for (XmlAttribute attribute : tag.getAttributes()) { final String value = attribute.getValue(); if (value != null && value.endsWith("px")) { final String newValue = convertToDp(value, dpi); if (newValue != null) { attribute.setValue(newValue); } } } final XmlTagValue tagValueElement = tag.getValue(); final String tagValue = tagValueElement.getText(); if (tagValue.endsWith("px")) { final String newValue = convertToDp(tagValue, dpi); if (newValue != null) { tagValueElement.setText(newValue); } } } private static String convertToDp(String value, int dpi) { String newValue = null; final Matcher matcher = PX_ATTR_VALUE_PATTERN.matcher(value); if (matcher.matches()) { final String numberString = matcher.group(1); try { final int px = Integer.parseInt(numberString); final int dp = px * 160 / dpi; newValue = Integer.toString(dp) + "dp"; } catch (NumberFormatException nufe) { LOG.error(nufe); } } return newValue; } @NotNull private static String getLabelForDensity(@NotNull Density density) { return String.format("%1$s (%2$d)", density.getShortDisplayValue(), density.getDpiValue()); } @Override public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.ContextType contextType) { return contextType != AndroidQuickfixContexts.BatchContext.TYPE && PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null; } @NotNull @Override public String getName() { return AndroidBundle.message("android.lint.fix.convert.to.dp"); } }