package openfoodfacts.github.scrachx.openfood.fragments; import android.content.Intent; import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.text.SpannableStringBuilder; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.StyleSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.afollestad.materialdialogs.MaterialDialog; import com.squareup.picasso.Picasso; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import butterknife.BindView; import butterknife.OnClick; import openfoodfacts.github.scrachx.openfood.R; import openfoodfacts.github.scrachx.openfood.models.Additive; import openfoodfacts.github.scrachx.openfood.models.AdditiveDao; import openfoodfacts.github.scrachx.openfood.models.Product; import openfoodfacts.github.scrachx.openfood.models.ProductImage; import openfoodfacts.github.scrachx.openfood.models.State; import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient; import openfoodfacts.github.scrachx.openfood.utils.Utils; import openfoodfacts.github.scrachx.openfood.views.FullScreenImage; import pl.aprilapps.easyphotopicker.DefaultCallback; import pl.aprilapps.easyphotopicker.EasyImage; import static android.Manifest.permission.CAMERA; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; import static openfoodfacts.github.scrachx.openfood.models.ProductImageField.FRONT; import static openfoodfacts.github.scrachx.openfood.models.ProductImageField.INGREDIENTS; import static openfoodfacts.github.scrachx.openfood.utils.Utils.MY_PERMISSIONS_REQUEST_CAMERA; import static openfoodfacts.github.scrachx.openfood.utils.Utils.bold; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotEmpty; public class IngredientsProductFragment extends BaseFragment { public static final Pattern INGREDIENT_PATTERN = Pattern.compile("[\\p{L}\\p{Nd}(),.-]+"); public static final Pattern ALLERGEN_PATTERN = Pattern.compile("[\\p{L}\\p{Nd}]+"); @BindView(R.id.textIngredientProduct) TextView ingredientsProduct; @BindView(R.id.textSubstanceProduct) TextView substanceProduct; @BindView(R.id.textTraceProduct) TextView traceProduct; @BindView(R.id.textAdditiveProduct) TextView additiveProduct; @BindView(R.id.textPalmOilProduct) TextView palmOilProduct; @BindView(R.id.textMayBeFromPalmOilProduct) TextView mayBeFromPalmOilProduct; @BindView(R.id.imageViewIngredients) ImageView mImageIngredients; @BindView(R.id.addPhotoLabel) TextView addPhotoLabel; private OpenFoodAPIClient api; private String mUrlImage; private State mState; private String barcode; private AdditiveDao mAdditiveDao; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { api = new OpenFoodAPIClient(getActivity()); return createView(inflater, container, R.layout.fragment_ingredients_product); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); Intent intent = getActivity().getIntent(); mState = (State) intent.getExtras().getSerializable("state"); mAdditiveDao = Utils.getAppDaoSession(getActivity()).getAdditiveDao(); final Product product = mState.getProduct(); barcode = product.getCode(); if (isNotBlank(product.getImageIngredientsUrl())) { addPhotoLabel.setVisibility(View.GONE); Picasso.with(view.getContext()) .load(product.getImageIngredientsUrl()) .into(mImageIngredients); mUrlImage = product.getImageIngredientsUrl(); } List<String> allergens = getAllergens(); if(mState != null && product.getIngredientsText() != null) { SpannableStringBuilder txtIngredients = new SpannableStringBuilder(product.getIngredientsText().replace("_","")); txtIngredients = setSpanBoldBetweenTokens(txtIngredients, allergens); int ingredientsListAt = Math.max(0, txtIngredients.toString().indexOf(":")); if(!txtIngredients.toString().substring(ingredientsListAt).trim().isEmpty()) { ingredientsProduct.setText(txtIngredients); } else { ingredientsProduct.setVisibility(View.GONE); } } if(!allergens.isEmpty()) { substanceProduct.append(bold(getString(R.string.txtSubstances))); substanceProduct.append(" "); String delim = ""; for (String allergen : allergens) { substanceProduct.append(delim); substanceProduct.append(allergen); delim = ", "; } } else { substanceProduct.setVisibility(View.GONE); } String traces; if (product.getTraces() == null) { traceProduct.setVisibility(View.GONE); } else { traces = product.getTraces().replace(",", ", "); if(traces.isEmpty()) { traceProduct.setVisibility(View.GONE); } else { traceProduct.append(bold(getString(R.string.txtTraces))); traceProduct.append(" "); traceProduct.append(traces); } } if(!product.getAdditivesTags().isEmpty()) { additiveProduct.setMovementMethod(LinkMovementMethod.getInstance()); additiveProduct.append(bold(getString(R.string.txtAdditives))); additiveProduct.append(" "); for (String tag : product.getAdditivesTags()) { String tagWithoutLocale = tag.replaceAll("(en:|fr:)", "").toUpperCase(); additiveProduct.append(getSpanTag(tagWithoutLocale, view)); } } else { additiveProduct.setVisibility(View.GONE); } if (product.getIngredientsFromPalmOilN() == 0 && product.getIngredientsFromOrThatMayBeFromPalmOilN() == 0) { palmOilProduct.setVisibility(View.GONE); mayBeFromPalmOilProduct.setVisibility(View.GONE); } else { if (!product.getIngredientsFromPalmOilTags().isEmpty()) { palmOilProduct.append(bold(getString(R.string.txtPalmOilProduct))); palmOilProduct.append(" "); palmOilProduct.append(product.getIngredientsFromPalmOilTags().toString().replaceAll("[\\[,\\]]", "")); } else { palmOilProduct.setVisibility(View.GONE); } if (!product.getIngredientsThatMayBeFromPalmOilTags().isEmpty()) { mayBeFromPalmOilProduct.append(bold(getString(R.string.txtMayBeFromPalmOilProduct))); mayBeFromPalmOilProduct.append(" "); mayBeFromPalmOilProduct.append(product.getIngredientsThatMayBeFromPalmOilTags().toString().replaceAll("[\\[,\\]]", "")); } else { mayBeFromPalmOilProduct.setVisibility(View.GONE); } } } private CharSequence getSpanTag(String tag, final View view) { final SpannableStringBuilder ssb = new SpannableStringBuilder(); final List<Additive> la = mAdditiveDao.queryBuilder().where(AdditiveDao.Properties.Code.eq(tag.toUpperCase())).list(); if (la.size() >= 1) { final Additive additive = la.get(0); ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(View v) { new MaterialDialog.Builder(view.getContext()) .title(additive.getCode() + " : " + additive.getName()) .content(additive.getRisk().toUpperCase()) .positiveText(R.string.txtOk) .show(); } }; ssb.append(tag); ssb.setSpan(clickableSpan, 0, ssb.length(), SPAN_EXCLUSIVE_EXCLUSIVE); ssb.append(" "); } return ssb; } private SpannableStringBuilder setSpanBoldBetweenTokens(CharSequence text, List<String> allergens) { final SpannableStringBuilder ssb = new SpannableStringBuilder(text); Matcher m = INGREDIENT_PATTERN.matcher(ssb); while (m.find()) { final String tm = m.group(); final String allergenValue = tm.replaceAll("[(),.-]+", ""); for (String allergen: allergens) { if(allergen.equalsIgnoreCase(allergenValue)) { int start = m.start(); int end = m.end(); if(tm.contains("(")) { start += 1; } else if(tm.contains(")")) { end -= 1; } ssb.setSpan(new StyleSpan(Typeface.BOLD), start, end, SPAN_EXCLUSIVE_EXCLUSIVE); } } } ssb.insert(0, Utils.bold(getString(R.string.txtIngredients) + ' ')); return ssb; } private List<String> getAllergens() { if (mState.getProduct() == null || mState.getProduct().getAllergens() == null) { return Collections.emptyList(); } List<String> list = new ArrayList<>(); Matcher m = ALLERGEN_PATTERN.matcher(mState.getProduct().getAllergens().replace(",", "")); while (m.find()) { final String tma = m.group(); boolean canAdd = true; for (String allergen : list) { if(tma.equalsIgnoreCase(allergen)){ canAdd = false; break; } } if (canAdd) { list.add(tma); } } return list; } @OnClick(R.id.imageViewIngredients) public void openFullScreen(View v) { if (mUrlImage != null) { Intent intent = new Intent(v.getContext(), FullScreenImage.class); Bundle bundle = new Bundle(); bundle.putString("imageurl", mUrlImage); intent.putExtras(bundle); startActivity(intent); } else { // take a picture if (ContextCompat.checkSelfPermission(getActivity(), CAMERA) != PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity(), new String[]{CAMERA}, MY_PERMISSIONS_REQUEST_CAMERA); } else { EasyImage.openCamera(this, 0); } } } private void onPhotoReturned(File photoFile) { ProductImage image = new ProductImage(barcode, INGREDIENTS, photoFile); api.postImg(getContext(), image); addPhotoLabel.setVisibility(View.GONE); mUrlImage = photoFile.getAbsolutePath(); Picasso.with(getContext()) .load(photoFile) .fit() .into(mImageIngredients); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); EasyImage.handleActivityResult(requestCode, resultCode, data, getActivity(), new DefaultCallback() { @Override public void onImagePickerError(Exception e, EasyImage.ImageSource source, int type) { //Some error handling } @Override public void onImagesPicked(List<File> imageFiles, EasyImage.ImageSource source, int type) { onPhotoReturned(imageFiles.get(0)); } @Override public void onCanceled(EasyImage.ImageSource source, int type) { //Cancel handling, you might wanna remove taken photo if it was canceled if (source == EasyImage.ImageSource.CAMERA) { File photoFile = EasyImage.lastlyTakenButCanceledPhoto(getContext()); if (photoFile != null) photoFile.delete(); } } }); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_CAMERA: { if (grantResults.length <= 0 || grantResults[0] != PERMISSION_GRANTED) { new MaterialDialog.Builder(getActivity()) .title(R.string.permission_title) .content(R.string.permission_denied) .negativeText(R.string.txtNo) .positiveText(R.string.txtYes) .onPositive((dialog, which) -> { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null); intent.setData(uri); startActivity(intent); }) .show(); } else { EasyImage.openCamera(this, 0); } } } } }