package com.quran.labs.androidquran.ui.fragment; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; import com.quran.labs.androidquran.R; import com.quran.labs.androidquran.data.Constants; import com.quran.labs.androidquran.data.QuranInfo; import com.quran.labs.androidquran.ui.PagerActivity; import com.quran.labs.androidquran.ui.QuranActivity; import com.quran.labs.androidquran.util.QuranUtils; import com.quran.labs.androidquran.widgets.ForceCompleteTextView; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import timber.log.Timber; public class JumpFragment extends DialogFragment { public static final String TAG = "JumpFragment"; public JumpFragment() { } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Activity activity = getActivity(); LayoutInflater inflater = activity.getLayoutInflater(); @SuppressLint("InflateParams") View layout = inflater.inflate(R.layout.jump_dialog, null); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(activity.getString(R.string.menu_jump)); // Sura chooser final ForceCompleteTextView suraInput = (ForceCompleteTextView) layout.findViewById( R.id.sura_spinner); final String[] suras = activity.getResources().getStringArray(R.array.sura_names); StringBuilder sb = new StringBuilder(); for (int i = 0; i < suras.length; i++) { sb.append(QuranUtils.getLocalizedNumber(activity, (i + 1))); sb.append(". "); sb.append(suras[i]); suras[i] = sb.toString(); sb.setLength(0); } InfixFilterArrayAdapter suraAdapter = new InfixFilterArrayAdapter(activity, android.R.layout.simple_spinner_dropdown_item, suras); suraInput.setAdapter(suraAdapter); // Ayah chooser final EditText ayahInput = (EditText) layout.findViewById(R.id.ayah_spinner); // Page chooser final EditText pageInput = (EditText) layout.findViewById(R.id.page_number); pageInput.setOnEditorActionListener((v, actionId, event) -> { boolean handled = false; if (actionId == EditorInfo.IME_ACTION_GO) { dismiss(); goToPage(pageInput.getText().toString()); handled = true; } return handled; }); suraInput.setOnItemClickListener((parent, view, position, rowId) -> { List<String> suraList = Arrays.asList(suras); String enteredText = suraInput.getText().toString(); String suraName; if (position >= 0) { // user selects suraName = suraAdapter.getItem(position); } else if (suraList.contains(enteredText)) { suraName = enteredText; } else if (suraAdapter.isEmpty()) { suraName = null; // leave to the next code } else { // maybe first initialization or invalid input suraName = suraAdapter.getItem(0); } int sura = suraList.indexOf(suraName) + 1; if (sura == 0) sura = 1; // default to al-Fatiha suraInput.setTag(sura); suraInput.setText(suras[sura - 1]); // trigger ayah change CharSequence ayahValue = ayahInput.getText(); // space is intentional, to differentiate with value set by the user (delete/backspace) ayahInput.setText(ayahValue.length() > 0 ? ayahValue : " "); }); ayahInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { Context context = getActivity(); String ayahString = s.toString(); int ayah = parseInt(ayahString, 1); Object suraTag = suraInput.getTag(); if (suraTag != null) { int sura = (int) suraTag; int ayahCount = QuranInfo.getNumAyahs(sura); ayah = Math.max(1, Math.min(ayahCount, ayah)); // ensure in 1..ayahCount int page = QuranInfo.getPageFromSuraAyah(sura, ayah); pageInput.setHint(QuranUtils.getLocalizedNumber(context, page)); pageInput.setText(null); } ayahInput.setTag(ayah); // seems numeric IM always use western arabic (not localized) String correctText = String.valueOf(ayah); // empty input means the user clears the input, we don't force to fill it, let him type if (s.length() > 0 && !correctText.equals(ayahString)) { s.replace(0, s.length(), correctText); } } }); builder.setView(layout); builder.setPositiveButton(getString(R.string.dialog_ok), (dialog, which) -> { try { dismiss(); String pageStr = pageInput.getText().toString(); if (TextUtils.isEmpty(pageStr)) { pageStr = pageInput.getHint().toString(); int page = Integer.parseInt(pageStr); int selectedSura = (int) suraInput.getTag(); int selectedAyah = (int) ayahInput.getTag(); if (activity instanceof QuranActivity) { ((QuranActivity) activity).jumpToAndHighlight(page, selectedSura, selectedAyah); } else if (activity instanceof PagerActivity) { ((PagerActivity) activity).jumpToAndHighlight(page, selectedSura, selectedAyah); } } else { goToPage(pageStr); } } catch (Exception e) { Timber.d(e, "Could not jump, something went wrong..."); } }); return builder.create(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } private void goToPage(String text) { int page; try { page = Integer.parseInt(text); } catch (NumberFormatException nfe) { // this can happen if we are coming from IME_ACTION_GO return; } // user has interacted with 'Go to page' field, so we // need to verify if the input number is within // the acceptable range if (page < Constants.PAGES_FIRST || page > Constants.PAGES_LAST) { // maybe show a toast message? return; } Activity activity = getActivity(); if (activity instanceof QuranActivity) { ((QuranActivity) activity).jumpTo(page); } else if (activity instanceof PagerActivity) { ((PagerActivity) activity).jumpTo(page); } } static int parseInt(String s, int defaultValue) { // May be extracted to util try { return Integer.parseInt(s); } catch (NumberFormatException e) { return defaultValue; } } /** * ListAdapter that supports filtering by using case-insensitive infix (substring). */ private static class InfixFilterArrayAdapter extends BaseAdapter implements Filterable { // May be extracted to other package private List<String> originalItems; private List<String> items; private LayoutInflater inflater; private int itemLayoutRes; private Filter filter = new ItemFilter(); private final Object lock = new Object(); InfixFilterArrayAdapter(@NonNull Context context, @LayoutRes int itemLayoutRes, @NonNull String[] items) { this.items = originalItems = Arrays.asList(items); this.inflater = LayoutInflater.from(context); this.itemLayoutRes = itemLayoutRes; } @Override public int getCount() { return items.size(); } @Override public String getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(itemLayoutRes, parent, false); } // As no fieldId is known/assigned, assume it is a TextView TextView text = (TextView) convertView; text.setText(getItem(position)); return convertView; } @Override public Filter getFilter() { return filter; } /** * Filter that do filtering by matching case-insensitive infix of the input. */ private class ItemFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { final FilterResults results = new FilterResults(); // The items never change after construction, not sure if really needs to copy final ArrayList<String> copy; synchronized (lock) { copy = new ArrayList<>(originalItems); } if (constraint == null || constraint.length() == 0) { results.values = copy; results.count = copy.size(); } else { final String infix = constraint.toString().toLowerCase(); final ArrayList<String> filteredCopy = new ArrayList<>(); for (String i : copy) { if (i == null) continue; String value = i.toLowerCase(); if (value.contains(infix)) { filteredCopy.add(i); } } results.values = filteredCopy; results.count = filteredCopy.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked items = (List<String>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } } }