public enum FilterMethod {
    DO_NOTHING, RECYCLER_PADDING, RECYCLER_NO_PADDING,
} public final class DictionaryFragmentsAdapter extends FragmentStateAdapter {
    private final ArrayList<DictionaryFragment> fragmentList;
    private final ArrayList<String> titlesList;

    public DictionaryFragmentsAdapter(@NonNull final FragmentActivity fragmentActivity, final int tabsSize) {
        super(fragmentActivity);
        fragmentList = new ArrayList<>(tabsSize);
        titlesList = new ArrayList<>(tabsSize);
    }

    public void addFragment(final String title) {
        fragmentList.add(new DictionaryFragment());
        titlesList.add(title);
    }

    public void setFragments(@NonNull final String... titles) {
        fragmentList.clear();
        titlesList.clear();

        for (final String title : titles) {
            fragmentList.add(new DictionaryFragment());
            titlesList.add(title);
        }
    }

    public boolean isEmpty() {
        return titlesList.isEmpty() || fragmentList.isEmpty();
    }

    @NonNull
    @Override
    public Fragment createFragment(final int position) {
        return fragmentList.get(position);
    }

    public DictionaryFragment getItem(final int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getItemCount() {
        return fragmentList.size();
    }

    public String getPageTitle(final int position) {
        return titlesList.get(position);
    }

    public int fragmentIndex(@NonNull final CharSequence title) {
        for (int i = 0; i < titlesList.size(); i++)
            if (title.equals(titlesList.get(i))) return i;
        return -1;
    }
} public final class SearchAdapter extends RecyclerView.Adapter<ResultViewHolder> implements Filterable {
    private final Filter filter;
    private final SearchHistoryTable historyDatabase;
    private final View.OnClickListener onClickListener;
    private final View.OnLongClickListener onLongClickListener;
    private List<SearchItem> suggestionsList = new ArrayList<>(0);
    private List<SearchItem> resultList = new ArrayList<>(0);
    private LayoutInflater layoutInflater;
    private View viewDivider;
    private String key = "";

    public SearchAdapter(@Nullable final SearchHistoryTable table, @NonNull final AdapterClickListener clickListener) {
        this.historyDatabase = table;
        this.onClickListener = clickListener;
        this.onLongClickListener = clickListener;
        this.filter = new Filter() {
            private final FilterResults filterResults = new FilterResults();

            @NonNull
            @Override
            protected FilterResults performFiltering(final CharSequence constraint) {
                filterResults.values = null;
                filterResults.count = 0;

                if (Utils.isEmpty(constraint)) key = "";
                else {
                    key = constraint.toString().toLowerCase(Utils.defaultLocale);

                    final List<SearchItem> history = new ArrayList<>(0);
                    if (historyDatabase != null) {
                        final List<SearchItem> databaseAllItems = historyDatabase.getAllItems(null);
                        if (databaseAllItems != null && !databaseAllItems.isEmpty())
                            history.addAll(databaseAllItems);
                    }
                    history.addAll(suggestionsList);

                    final ArrayList<SearchItem> results = new ArrayList<>(0);
                    for (final SearchItem item : history) {
                        boolean containsSearch = false;
                        for (int i = 0; i < results.size(); i++) {
                            final SearchItem searchItem = results.get(i);
                            if (searchItem != null && (searchItem == item || searchItem.equals(item) ||
                                    TextUtils.equals(searchItem.getText(), item.getText()))) {
                                containsSearch = true;
                                break;
                            }
                        }

                        if (!containsSearch && item.getText().toLowerCase().contains(key))
                            results.add(item);
                    }

                    filterResults.values = results;
                    filterResults.count = results.size();
                }

                if (viewDivider != null) viewDivider.setVisibility(filterResults.values == null ||
                        filterResults.count < 1 ? View.GONE : View.VISIBLE);

                return filterResults;
            }

            @Override
            protected void publishResults(final CharSequence constraint, FilterResults results) {
                if (results == null) results = filterResults;

                //noinspection unchecked
                final List<SearchItem> dataSet = results.count > 0 ? (List<SearchItem>) results.values
                        : key.isEmpty() && historyDatabase != null ? historyDatabase.getAllItems(null)
                        : null;
                setData(dataSet);

                filterResults.values = null;
                filterResults.count = 0;
            }
        };
        this.filter.filter("");
    }

    public void setSuggestionsList(final List<SearchItem> suggestionsList) {
        this.suggestionsList = suggestionsList;
        getFilter().filter(key);
    }

    @Override
    public Filter getFilter() {
        return filter;
    }

    private void setData(final List<SearchItem> data) {
        final int previousSize = getItemCount();
        final int nextSize = data == null ? 0 : data.size();
        resultList = data;
        if (previousSize == 0 && nextSize == 0) return;
        if (previousSize == nextSize) notifyItemRangeChanged(0, previousSize);
        else if (previousSize <= nextSize) {
            notifyItemRangeChanged(0, previousSize);
            notifyItemRangeInserted(previousSize, nextSize - previousSize);
        } else if (nextSize != 0) {
            notifyItemRangeChanged(0, nextSize);
            notifyItemRangeRemoved(nextSize - 1, previousSize);
        } else
            notifyItemRangeRemoved(0, previousSize);
    }

    @NonNull
    @Override
    public ResultViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
        if (layoutInflater == null)
            layoutInflater = LayoutInflater.from(parent.getContext());
        return new ResultViewHolder(SearchItemBinding.inflate(layoutInflater, parent, false),
                onClickListener, onLongClickListener);
    }

    @Override
    public void onBindViewHolder(@NonNull final ResultViewHolder viewHolder, final int position) {
        final SearchItem item = resultList.get(position);
        final SearchItemBinding itemBinding = viewHolder.searchItemBinding;

        viewHolder.itemView.setTag(item);

        itemBinding.ivIcon.setImageResource(item.getIcon());
        itemBinding.ivIcon.setColorFilter(MaterialSearchView.iconColor, PorterDuff.Mode.SRC_IN);

        final String itemText = item.getText();
        final String itemTextLower = itemText.toLowerCase(Utils.defaultLocale);

        if (!itemTextLower.contains(key) || key.isEmpty()) itemBinding.tvText.setText(itemText);
        else {
            final SpannableString highlightedText = new SpannableString(itemText);
            final int keyIndex = itemTextLower.indexOf(key);
            highlightedText.setSpan(new ForegroundColorSpan(0xA72196F3),
                    keyIndex, keyIndex + key.length(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            itemBinding.tvText.setText(highlightedText, TextView.BufferType.SPANNABLE);
        }
    }

    @Override
    public int getItemCount() {
        return resultList == null ? 0 : resultList.size();
    }

    @Override
    public int getItemViewType(final int position) {
        return position;
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        if (layoutInflater == null)
            layoutInflater = LayoutInflater.from(recyclerView.getContext());
        final ViewParent parent = recyclerView.getParent();
        if (parent instanceof View) viewDivider = ((View) parent).findViewById(R.id.view_divider);
    }
} public final class WordItem {
    private int position = RecyclerView.NO_POSITION;
    private final String word;
    private final String[][] defs;
    private final String parsedTags;
    private boolean expanded;

    public WordItem(final String word, final int numSyllables, final String[] tags, final String[][] defs) {
        this.word = word;
        this.defs = defs;

        final StringBuilder tagsBuilder = new StringBuilder();
        if (tags != null && tags.length > 0) {
            tagsBuilder.insert(0, "tags:");
            for (final String tag : tags) {
                if (tag.equals("syn")) tagsBuilder.insert(5, " [synonym]");
                if (tag.equals("prop")) tagsBuilder.insert(5, " [proper]");
                if (tag.equals("n")) tagsBuilder.append(" noun,");
                if (tag.equals("adj")) tagsBuilder.append(" adjective,");
                if (tag.equals("v")) tagsBuilder.append(" verb,");
                if (tag.equals("adv")) tagsBuilder.append(" adverb,");
            }
        }

        final int lastCharIndex = tagsBuilder.length() - 1;
        if (lastCharIndex > 1 && tagsBuilder.charAt(lastCharIndex) == ',')
            tagsBuilder.deleteCharAt(lastCharIndex);

        tagsBuilder.append(tags != null && tags.length > 0 && numSyllables > 0 ? '\n' : '\0')
                .append("syllables: ").append(numSyllables);

        this.parsedTags = tagsBuilder.toString();
    }

    public String getWord() {
        return word;
    }

    public String getParsedTags() {
        return parsedTags;
    }

    public String[][] getDefs() {
        return defs;
    }

    public boolean isExpanded() {
        return expanded;
    }

    public void setExpanded(final boolean expanded) {
        this.expanded = expanded;
    }

    public void setPosition(final int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }
} public final class SearchAsyncTask extends LocalAsyncTask<String, ArrayList<WordItem>> {
    private MainCheck mainCheck;

    public SearchAsyncTask(final MainCheck mainCheck) {
        this.mainCheck = mainCheck;
    }

    @NonNull
    @Override
    protected ArrayList<WordItem> doInBackground(final String param) {
        final ArrayList<WordItem> arrayList = new ArrayList<>(0);

        try {
            final String response = Utils.getResponse("https://api.data".concat("muse.com/sug?s=")
                    .concat(URLEncoder.encode(param)));

            if (response != null) {
                final JSONArray jsonArray = new JSONArray(response);

                for (int i = 0; i < jsonArray.length(); ++i) {
                    final JSONObject jsonObject = jsonArray.getJSONObject(i);
                    arrayList.add(new WordItem(jsonObject.getString("word"), 0, null, null));
                }
            }
        } catch (final Exception e) {
            if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "SearchAsyncTask", e);
            else Utils.firebaseCrashlytics.recordException(e);
        }

        return arrayList;
    }

    @Override
    protected void onCancelled(@NonNull final ArrayList<WordItem> wordItems) {
        if (mainCheck != null) mainCheck.afterSearch(null);
        mainCheck = null;
    }

    @Override
    protected void onPostExecute(final ArrayList<WordItem> result) {
        if (mainCheck != null) mainCheck.afterSearch(result);
    }
} public final class SearchHistoryTable {
    private final static int HISTORY_SIZE = 8;
    private final SearchHistoryDatabase dbHelper;
    private SQLiteDatabase db;

    public SearchHistoryTable(final Context context) {
        dbHelper = new SearchHistoryDatabase(context, this);
        open();
    }

    public void open() {
        if ((db == null || !db.isOpen()) && dbHelper != null) db = dbHelper.getWritableDatabase();
    }

    public void close() {
        if (db != null && db.isOpen()) dbHelper.close();
    }

    public void addItem(final SearchItem item) {
        if (db != null && db.isOpen()) {
            final String text = item.getText();
            final ContentValues values = new ContentValues();
            if (checkText(text)) {
                values.put("_id", getLastItemId() + 1);
                db.update("search_history", values, "_id = ?", new String[]{Integer.toString(getItemId(item))});
            } else {
                values.put("_text", text);
                db.insert("search_history", null, values);
            }
        }
    }

    @Nullable
    public List<SearchItem> getAllItems(final String databaseKey) {
        List<SearchItem> list = null;

        if (db == null || !db.isOpen()) db = dbHelper.getWritableDatabase();

        if (db != null && db.isOpen()) {
            final boolean isKeyEmpty = Utils.isEmpty(databaseKey);
            try (final Cursor cursor = db.rawQuery(isKeyEmpty ? "SELECT * FROM search_history ORDER BY _id DESC LIMIT ?"
                            : "SELECT * FROM search_history WHERE _text = ?",
                    new String[]{isKeyEmpty ? Integer.toString(HISTORY_SIZE) : databaseKey})) {
                if (cursor != null && cursor.moveToFirst()) {
                    list = new ArrayList<>(0);
                    do list.add(new SearchItem(R.drawable.ic_history, cursor.getString(1), false));
                    while (cursor.moveToNext());
                }
            }
        }

        return list;
    }

    public void clearDatabase(final String key) {
        if (db != null && db.isOpen()) {
            if (key == null) db.delete("search_history", null, null);
            else db.delete("search_history", "_text = ?", new String[]{key});
        }
    }

    /*
    enable?
    public int getItemsCount() {
        if (db == null || !db.isOpen()) return -1;

        final Cursor cursor = db.rawQuery("SELECT * FROM search_history", null);
        final int count = cursor.getCount();
        cursor.close();
        return count;
    }
    */

    private int getItemId(@NonNull final SearchItem item) {
        final int id;
        try (final Cursor res = db.rawQuery("SELECT _id FROM search_history WHERE _text = ?", new String[]{item.getText()})) {
            res.moveToFirst();
            id = res.getInt(0);
        }
        return id;
    }

    private int getLastItemId() {
        try (final Cursor cursor = db.rawQuery("SELECT _id FROM search_history", null)) {
            return cursor != null && cursor.moveToLast() ? cursor.getInt(0) : 0;
        }
    }

    private boolean checkText(final String text) {
        try (final Cursor cursor = db.rawQuery("SELECT _text FROM search_history WHERE _text = ?", new String[]{text})) {
            return cursor != null && cursor.moveToFirst();
        }
    }

    private final static class SearchHistoryDatabase extends SQLiteOpenHelper {
        private final SearchHistoryTable searchHistoryTable;

        SearchHistoryDatabase(final Context context, final SearchHistoryTable searchHistoryTable) {
            super(context, "search_history_database.db", null, 4);
            this.searchHistoryTable = searchHistoryTable;
        }

        @Override
        public void onCreate(@NonNull final SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF NOT EXISTS search_history ( _id INTEGER PRIMARY KEY AUTOINCREMENT, _text TEXT );");
        }

        @Override
        public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, final int newVersion) {
            List<SearchItem> searchItems = null;
            if (searchHistoryTable != null) searchItems = searchHistoryTable.getAllItems(null);

            db.execSQL("DROP TABLE IF EXISTS search_history;");
            onCreate(db);

            if (db.isOpen() && searchHistoryTable != null && searchItems != null && searchItems.size() > 0)
                for (final SearchItem searchItem : searchItems)
                    searchHistoryTable.addItem(searchItem);
        }

        @Override
        public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion, final int newVersion) {
            onUpgrade(db, oldVersion, newVersion);
        }
    }
} public final class AdvancedDialog extends Dialog implements CompoundButton.OnCheckedChangeListener {
    private final boolean[] enabledChecks = {true, true, true, true, false, false, false, false, false, false};
    private final AdvancedDialogBinding dialogBinding;
    private final FragmentLoader fragmentLoader;
    private List<CompoundButton> checkBoxes;

    public AdvancedDialog(final Main act) {
        super(act, R.style.DefinitionsDialogTheme);
        this.dialogBinding = AdvancedDialogBinding.inflate(act.getLayoutInflater());
        this.fragmentLoader = act;
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setCancelable(true);
        setCanceledOnTouchOutside(true);

        final Window window = getWindow();
        if (window != null) {
            window.requestFeature(Window.FEATURE_NO_TITLE);
            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        setContentView(dialogBinding.getRoot());

        dialogBinding.alertTitle.setText(R.string.advance);

        final CheckBox cbReverse = (CheckBox) dialogBinding.checkboxContainer.getChildAt(0);
        final CheckBox cbSoundsLike = (CheckBox) dialogBinding.checkboxContainer.getChildAt(1);
        final CheckBox cbSpelledLike = (CheckBox) dialogBinding.checkboxContainer.getChildAt(2);
        final CheckBox cbSynonyms = (CheckBox) dialogBinding.checkboxContainer.getChildAt(3);
        final CheckBox cbAntonyms = (CheckBox) dialogBinding.checkboxContainer.getChildAt(4);
        final CheckBox cbTriggers = (CheckBox) dialogBinding.checkboxContainer.getChildAt(5);
        final CheckBox cbPartOf = (CheckBox) dialogBinding.checkboxContainer.getChildAt(6);
        final CheckBox cbComprises = (CheckBox) dialogBinding.checkboxContainer.getChildAt(7);
        final CheckBox cbRhymes = (CheckBox) dialogBinding.checkboxContainer.getChildAt(8);
        final CheckBox cbHomophones = (CheckBox) dialogBinding.checkboxContainer.getChildAt(9);

        checkBoxes = Arrays.asList(new CheckBox[]{cbReverse, cbSoundsLike, cbSpelledLike,
                cbSynonyms, cbAntonyms, cbTriggers, cbPartOf, cbComprises, cbRhymes, cbHomophones});

        final boolean[] tabBools = SettingsHelper.getTabs();
        for (int i = 0; i < tabBools.length; i++) {
            final boolean bool = tabBools[i];
            checkBoxes.get(i).setChecked(bool);
            enabledChecks[i] = bool;
        }

        cbReverse.setOnCheckedChangeListener(this);
        cbSoundsLike.setOnCheckedChangeListener(this);
        cbSpelledLike.setOnCheckedChangeListener(this);
        cbSynonyms.setOnCheckedChangeListener(this);
        cbAntonyms.setOnCheckedChangeListener(this);
        cbTriggers.setOnCheckedChangeListener(this);
        cbPartOf.setOnCheckedChangeListener(this);
        cbComprises.setOnCheckedChangeListener(this);
        cbRhymes.setOnCheckedChangeListener(this);
        cbHomophones.setOnCheckedChangeListener(this);

        final View.OnClickListener clickListener = view -> {
            if (view == dialogBinding.btnOK) {
                if (getResult() != 0) {
                    SettingsHelper.setTabs(Arrays.toString(enabledChecks));
                    fragmentLoader.loadFragments(false);
                } else {
                    Toast.makeText(getContext(), R.string.select_a_option_first, Toast.LENGTH_SHORT).show();
                    return;
                }
            }
            dismiss();
        };
        dialogBinding.btnOK.setOnClickListener(clickListener);
        dialogBinding.btnCancel.setOnClickListener(clickListener);
    }

    @Override
    public void show() {
        super.show();
        final Window window = getWindow();
        if (window != null) window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onCheckedChanged(final CompoundButton compoundButton, final boolean b) {
        enabledChecks[checkBoxes.indexOf(compoundButton)] = b;
    }

    private int getResult() {
        int count = 0;
        for (final boolean enabled : enabledChecks)
            count = count + (enabled ? 1 : 0);
        return count;
    }
} public final class SettingsDialog extends Dialog {
    public final static int TTS_SETTINGS_REQUEST_CODE = 5320;
    private final int maxWords = SettingsHelper.getMaxWords();
    private final boolean showAds = SettingsHelper.showAds();
    private final boolean showPopup = SettingsHelper.showDefsPopup();
    private final boolean showDialog = SettingsHelper.showDialog();
    private final boolean showFloating = SettingsHelper.showFloating();
    private final boolean showFloatingDialog = SettingsHelper.showFloatingDialog();
    private final boolean wasTTSErrorShown;
    private final Activity activity;

    public SettingsDialog(final Activity act, final DialogInterface dialog) {
        super(act, R.style.DefinitionsDialogTheme);
        activity = act;
        wasTTSErrorShown = dialog != null;
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setCancelable(true);
        setCanceledOnTouchOutside(true);

        final Window window = getWindow();
        if (window != null) {
            window.requestFeature(Window.FEATURE_NO_TITLE);
            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        final SettingsDialogBinding dialogBinding = SettingsDialogBinding.inflate(getLayoutInflater());
        final LinearLayoutCompat rootView = dialogBinding.getRoot();

        dialogBinding.alertTitle.setText(R.string.settings);

        dialogBinding.numberPicker.setProgress(maxWords);
        dialogBinding.showFloating.setChecked(showFloating);
        dialogBinding.showAds.setChecked(showAds);
        dialogBinding.showWordDialog.setChecked(showDialog);
        dialogBinding.showDefsPopup.setChecked(showPopup);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            dialogBinding.showFloatingDialog.setEnabled(false);
            dialogBinding.showFloatingDialog.setVisibility(View.GONE);
        } else {
            dialogBinding.showFloatingDialog.setEnabled(showFloating);
            dialogBinding.showFloatingDialog.setAlpha(showFloating ? 1f : 0.6f);
            dialogBinding.showFloatingDialog.setChecked(showFloatingDialog);
            dialogBinding.showFloatingDialog.setVisibility(View.VISIBLE);
        }

        final int darkMode = SettingsHelper.getNightMode();
        final int checkedTheme;
        if (darkMode == AppCompatDelegate.MODE_NIGHT_YES) checkedTheme = R.id.rbThemeDark;
        else if (darkMode == AppCompatDelegate.MODE_NIGHT_NO) checkedTheme = R.id.rbThemeLight;
        else checkedTheme = R.id.rbThemeAuto;
        dialogBinding.rgAppTheme.check(checkedTheme);

        ((View) dialogBinding.btnTTS.getParent()).setVisibility(wasTTSErrorShown ? View.GONE : View.VISIBLE);

        final View.OnClickListener onClickListener = v -> {
            if (v instanceof MaterialCheckedTextView) {
                final MaterialCheckedTextView tvCheck = (MaterialCheckedTextView) v;
                final boolean checked = tvCheck.isChecked();
                tvCheck.setChecked(!checked);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && v == dialogBinding.showFloating) {
                    dialogBinding.showFloatingDialog.setEnabled(!checked);
                    dialogBinding.showFloatingDialog.setAlpha(!checked ? 1f : 0.6f);
                }
                return;
            }

            if (v == dialogBinding.btnOK) {
                int theme = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
                final int selectedTheme = dialogBinding.rgAppTheme.getCheckedButtonId();
                if (selectedTheme == R.id.rbThemeDark) theme = AppCompatDelegate.MODE_NIGHT_YES;
                else if (selectedTheme == R.id.rbThemeLight) theme = AppCompatDelegate.MODE_NIGHT_NO;

                SettingsHelper.setValues(Math.max(Math.min(1000, dialogBinding.numberPicker.getProgress()), 1),
                        theme, dialogBinding.showAds.isChecked(), dialogBinding.showWordDialog.isChecked(),
                        dialogBinding.showFloating.isChecked(), dialogBinding.showFloatingDialog.isChecked(),
                        dialogBinding.showDefsPopup.isChecked());

                if (activity instanceof Main) {
                    ((Main) activity).closeExpanded();
                    Utils.adsBox(activity);
                }

                if (selectedTheme != checkedTheme) {
                    AppCompatDelegate.setDefaultNightMode(theme);
                    if (activity instanceof Main) ((Main) activity).loadFragments(false);
                    else if (activity != null) activity.recreate();
                }
            } else if (v == dialogBinding.btnTTS) {
                if (activity != null)
                    activity.startActivityForResult(new Intent(activity, TTSActivity.class),
                            TTS_SETTINGS_REQUEST_CODE);
                return;
            } else if (activity instanceof Main) Utils.adsBox(activity);

            dismiss();
        };

        dialogBinding.showAds.setOnClickListener(onClickListener);
        dialogBinding.showWordDialog.setOnClickListener(onClickListener);
        dialogBinding.showFloating.setOnClickListener(onClickListener);
        dialogBinding.showFloatingDialog.setOnClickListener(onClickListener);
        dialogBinding.showDefsPopup.setOnClickListener(onClickListener);

        dialogBinding.btnOK.setOnClickListener(onClickListener);
        dialogBinding.btnTTS.setOnClickListener(onClickListener);
        dialogBinding.btnCancel.setOnClickListener(onClickListener);

        setContentView(rootView);

        rootView.post(() -> {
            dialogBinding.dummy.requestFocusFromTouch();
            dialogBinding.dummy.requestFocus();

            dialogBinding.dummy.clearFocus();
        });
    }

    @Override
    public void show() {
        super.show();
        final Window window = getWindow();
        if (window != null)
            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }
} @RequiresApi(api = 29)
public final class BubbleHelper {
    private final static HashSet<String> categorySet;

    static {
        categorySet = new HashSet<>();
        categorySet.add("android.shortcut.conversation");
        categorySet.add("awais.linkedwords.bubbles.SEARCH");
    }

    public static final String INTENT_EXTRA_BUBBLING = "awais.intent.BUBBLING";

    public static final String LW_BUBBLES_CHANNEL_NAME = "Linked Words Bubbles";
    public static final String LW_BUBBLES_CHANNEL_ID = "LinkedWordsBubbles";
    public static final String LW_BUBBLES_CONVO_ID = "LinkedWordsFloatingDict";

    public static final int LW_REQUEST_BUBBLE = 100425;
    public static final int LW_NOTIFICATION_ID = 100473;

    public static final int LW_BUBBLE_ICON = R.mipmap.ic_launcher;              // icon for bubbles shortcut icon
    public static final int LW_SMALL_ICON = R.mipmap.ic_launcher_foreground;    // icon for notification in status bar
    public static final int LW_SHORTCUT_ICON = R.drawable.ic_shortcut_search;   // icon for quick actions (shortcuts)

    private final WeakReference<Context> context;
    private final ComponentName componentName;
    private final LocusIdCompat locusIdCompat;
    private final IconCompat bubbleIcon, defaultShortcutIcon;
    private final float screenHeight;

    private String text;
    private Intent intent;
    private PendingIntent pendingIntent;

    public BubbleHelper(final Context context, final String text) {
        this.context = new WeakReference<>(context);

        this.locusIdCompat = new LocusIdCompat(LW_BUBBLES_CONVO_ID);
        this.bubbleIcon = IconCompat.createWithResource(context, LW_BUBBLE_ICON);
        this.defaultShortcutIcon = IconCompat.createWithResource(context, LW_SHORTCUT_ICON);

        this.componentName = (this.intent = new Intent(context, Main.class)
                .setAction(Intent.ACTION_VIEW)).getComponent();
        setText(text);

        this.screenHeight = getScreenHeight(context);
    }

    @SuppressLint("UnspecifiedImmutableFlag")
    public void setText(final String text) {
        this.text = text;

        final Context context = this.context.get();

        if (intent == null)
            intent = new Intent(context, Main.class).setAction(Intent.ACTION_VIEW);

        final Bundle extras = intent.getExtras();
        if (extras != null) extras.clear();

        int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT;
        // fix for Android S+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) flags |= PendingIntent.FLAG_MUTABLE;

        this.pendingIntent = PendingIntent.getActivity(context, LW_REQUEST_BUBBLE,
                intent.putExtra(INTENT_EXTRA_BUBBLING, true)
                        .putExtra(Intent.EXTRA_TEXT, text)
                        .setData(Uri.parse(text)), flags);
    }

    public void showBubble() {
        final Context context = this.context.get();
        if (notificationManager != null && context != null) {
            /*
             cancels the notification, not used atm
                try {
                    notificationManager.cancel(LW_NOTIFICATION_ID);
                } catch (final Exception e) {
                    // ignore
                }
            */

            final Person bubblePerson = new Person.Builder().setName(LW_BUBBLES_CHANNEL_NAME).build();

            final ShortcutInfoCompat.Builder shortcutBuilder = new ShortcutInfoCompat.Builder(context, LW_BUBBLES_CONVO_ID)
                    .setIcon(bubbleIcon).setShortLabel(context.getString(R.string.search)).setIntent(intent).setLongLived(true)
                    .setLocusId(locusIdCompat).setCategories(categorySet).setActivity(componentName);
            ShortcutManagerCompat.setDynamicShortcuts(context, Collections.singletonList(shortcutBuilder.build()));

            final BubbleMetadata bubbleMetadata = new BubbleMetadata.Builder(pendingIntent, bubbleIcon)
                    .setDesiredHeight(Math.round(screenHeight)).setSuppressNotification(true).setAutoExpandBubble(true).build();

            final Notification notification = new NotificationCompat.Builder(context, LW_BUBBLES_CHANNEL_ID)
                    .setSmallIcon(LW_SMALL_ICON).setOnlyAlertOnce(true).setAutoCancel(true).setColorized(true)
                    .setVibrate(null).setDefaults(0)
                    // bubbles api stuff
                    .setBubbleMetadata(bubbleMetadata).setShortcutId(LW_BUBBLES_CONVO_ID).setLocusId(locusIdCompat)
                    // category and style stuff
                    .setCategory(NotificationCompat.CATEGORY_MESSAGE).setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
                    .setStyle(new NotificationCompat.MessagingStyle(bubblePerson)
                            .addMessage(this.text, System.currentTimeMillis(), bubblePerson)
                    ).build();

            notification.flags |= Notification.FLAG_BUBBLE | Notification.FLAG_LOCAL_ONLY | Notification.FLAG_AUTO_CANCEL
                    | Notification.FLAG_ONLY_ALERT_ONCE;

            if (notificationManager.canNotifyAsPackage(BuildConfig.APPLICATION_ID))
                notificationManager.notifyAsPackage(BuildConfig.APPLICATION_ID, LW_BUBBLES_CONVO_ID,
                        LW_NOTIFICATION_ID, notification);
            else
                notificationManager.notify(LW_NOTIFICATION_ID, notification);

            final Handler handler = new Handler(Looper.getMainLooper());
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    // this is for setting search icon back to shortcut
                    shortcutBuilder.setIcon(defaultShortcutIcon);
                    final ShortcutInfoCompat shortcutInfoCompat = shortcutBuilder.build();
                    ShortcutManagerCompat.setDynamicShortcuts(context,
                            Collections.singletonList(shortcutInfoCompat));
                    handler.removeCallbacks(this);
                }
            }, 400);
        }
    }

    private static float getScreenHeight(final Context context) {
        return (context != null ? context.getResources() : Resources.getSystem())
                .getDisplayMetrics().heightPixels;
    }
} public final class MenuHelper {
    private static SpannableStringBuilder examplesSpan, helpSpan, licensesSpan;
    private static CustomTabsIntent.Builder customTabsIntent;
    private final MenuDialog menuDialog;
    private final String creditsString;
    private final FragmentManager fragmentManager;

    public MenuHelper(@NonNull final AppCompatActivity activity) {
        final Resources resources = activity.getResources();
        creditsString = resources.getString(R.string._credits);
        fragmentManager = activity.getSupportFragmentManager();
        menuDialog = new MenuDialog();
        customTabsIntent = new CustomTabsIntent.Builder();

        final boolean helpEmpty = helpSpan == null || helpSpan.length() < 1;
        final boolean examplesEmpty = examplesSpan == null || examplesSpan.length() < 1;
        final boolean licensesEmpty = licensesSpan == null || licensesSpan.length() < 1;

        if (helpEmpty || examplesEmpty || licensesEmpty) new LocalAsyncTask<Void, Void>() {
            @Nullable
            @Override
            protected Void doInBackground(final Void param) {
                final int helpColor = ResourcesCompat.getColor(resources, R.color.helper_color, null);

                if (examplesEmpty) {
                    // XXX EXAMPLES
                    final SpanBuilder examplesBuilder = new SpanBuilder();
                    examplesBuilder.append(activity.getString(R.string.finding_help).concat(":\n"), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor), new StyleSpan(Typeface.BOLD));
                    examplesBuilder.append("person who makes gold\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("one who massages\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("food search\n\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append(activity.getString(R.string.related_help).concat(":\n"), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor), new StyleSpan(Typeface.BOLD));
                    examplesBuilder.append("rainbow colors\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("tropical birds\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("spicy vegetables\n\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append(activity.getString(R.string.answers_help).concat(":\n"), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor), new StyleSpan(Typeface.BOLD));
                    examplesBuilder.append("what's popular city of Pakistan?\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("what's largest continent on earth?\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append("who was Galileo?\n\n", new BulletSpan(26, helpColor));
                    examplesBuilder.append(activity.getString(R.string.spelled_help).concat(":\n"), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor), new StyleSpan(Typeface.BOLD));
                    examplesBuilder.append("l?nd\t-- " + activity.getString(R.string.single_char_help, '?') + '\n', new BulletSpan(26, helpColor));
                    examplesBuilder.append("fr*g\t-- " + activity.getString(R.string.number_char_help, '*') + '\n', new BulletSpan(26, helpColor));
                    examplesBuilder.append("ta#t\t-- " + activity.getString(R.string.consonant_char_help, '#') + '\n', new BulletSpan(26, helpColor));
                    examplesBuilder.append("**stone**\t-- " + activity.getString(R.string.phrase_help) + '\n', new BulletSpan(26, helpColor));
                    examplesSpan = examplesBuilder.build();
                }

                if (helpEmpty) {
                    // XXX HELP
                    final SpanBuilder helpBuilder = new SpanBuilder();
                    helpBuilder.append(activity.getString(R.string.reverse).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.reverse_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.sounds_like).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.sounds_like_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.spelled_help).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.wildcards_help) + '\n', new BulletSpan(26, helpColor));
                    helpBuilder.append("[" + activity.getString(R.string.wildcard_link_help).concat("]\n\n"), new BulletSpan(26, helpColor), new ClickableSpan() {
                        @Override
                        public void onClick(@NonNull final View view) {
                            customTabsIntent.setToolbarColor(0xFFFFC400);
                            CustomTabActivityHelper.openCustomTab(activity, customTabsIntent.build(), Uri.parse("https://www.one".concat("look.com/?c=faq#patterns")));
                        }
                    });
                    helpBuilder.append(activity.getString(R.string.synonyms).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.synonym_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.antonyms).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.antonym_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.triggers).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.triggers_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.part_of).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.is_part_of_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.comprises).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.comprises_of_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.rhymes).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.rhymes_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpBuilder.append(activity.getString(R.string.homophones).concat(":\n"), new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new ForegroundColorSpan(helpColor));
                    helpBuilder.append(activity.getString(R.string.homophones_help).concat("\n\n"), new BulletSpan(26, helpColor));
                    helpSpan = helpBuilder.build();
                }

                if (licensesEmpty) {
                    // XXX LICENSES
                    final SpanBuilder licensesBuilder = new SpanBuilder();
                    licensesBuilder.append("App Icon:\n", new StyleSpan(Typeface.BOLD), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor));
                    licensesBuilder.append("Android Asset Studio - Launcher icon generator\n\n", new BulletSpan(26, helpColor), new ClickableSpan() {
                        @Override
                        public void onClick(@NonNull final View view) {
                            customTabsIntent.setToolbarColor(0xFF607D8B);
                            CustomTabActivityHelper.openCustomTab(activity, customTabsIntent.build(), Uri.parse("https://romannurik.github.io/AndroidAssetStudio/"));
                        }
                    });

                    licensesBuilder.append("Dictionary API:\n", new StyleSpan(Typeface.BOLD), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor));
                    licensesBuilder.append("Datamuse API\n\n", new BulletSpan(26, helpColor), new ClickableSpan() {
                        @Override
                        public void onClick(@NonNull final View view) {
                            customTabsIntent.setToolbarColor(0xFF006FCC);
                            CustomTabActivityHelper.openCustomTab(activity, customTabsIntent.build(), Uri.parse("https://www.datamuse.com/api/"));
                        }
                    });

                    licensesBuilder.append("Libraries:\n", new StyleSpan(Typeface.BOLD), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor));
                    licensesBuilder.append("SearchView [Apache License 2.0]\n", new BulletSpan(26, helpColor));
                    licensesBuilder.append("Chrome Custom Tabs [Apache License 2.0]\n", new BulletSpan(26, helpColor));
                    licensesBuilder.append("Expandable FAB [Apache License 2.0]\n\n", new BulletSpan(26, helpColor));

                    licensesBuilder.append("License:\n", new StyleSpan(Typeface.BOLD), new RelativeSizeSpan(1.2f), new ForegroundColorSpan(helpColor));
                    licensesBuilder.append("Apache License 2.0", new BulletSpan(26, helpColor), new ClickableSpan() {
                        @Override
                        public void onClick(@NonNull final View view) {
                            customTabsIntent.setToolbarColor(0xFFCB2533);
                            CustomTabActivityHelper.openCustomTab(activity, customTabsIntent.build(), Uri.parse("https://www.apache.org/licenses/LICENSE-2.0"));
                        }
                    });

                    licensesSpan = licensesBuilder.build();
                }

                return null;
            }
        }.execute();
    }

    public void show(@NonNull final MenuItem item) {
        final int itemId = item.getItemId();
        menuDialog.setTitle(itemId == R.id.mLicenses ? creditsString : item.getTitle());
        if (itemId == R.id.mExamples) menuDialog.setMessage(examplesSpan);
        else if (itemId == R.id.mHelp) menuDialog.setMessage(helpSpan);
        else if (itemId == R.id.mLicenses) menuDialog.setMessage(licensesSpan);
        else if (itemId == R.id.mAbout) menuDialog.setMessage("aboutHere");
        menuDialog.show(fragmentManager, null);
    }

    private final static class SpanBuilder {
        private final ArrayList<SpanSection> spanSections = new ArrayList<>(0);
        private final StringBuilder stringBuilder = new StringBuilder();

        void append(final String text, final Object... styles) {
            if (styles != null && styles.length > 0)
                spanSections.add(new SpanSection(text, stringBuilder.length(), styles));
            stringBuilder.append(text);
        }

        @NonNull
        SpannableStringBuilder build() {
            final SpannableStringBuilder ssb = new SpannableStringBuilder(stringBuilder);
            for (final SpanSection section : spanSections) section.apply(ssb);
            return ssb;
        }

        @NonNull
        @Override
        public String toString() {
            return String.valueOf(build());
        }

        private final static class SpanSection {
            private final String text;
            private final int startIndex;
            private final Object[] styles;

            private SpanSection(final String text, final int startIndex, final Object... styles) {
                this.styles = styles;
                this.text = text;
                this.startIndex = startIndex;
            }

            private void apply(final SpannableStringBuilder spanStringBuilder) {
                if (spanStringBuilder != null) {
                    for (final Object style : styles)
                        spanStringBuilder.setSpan(style, startIndex, startIndex + text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                }
            }
        }
    }
} public final class SettingsHelper {
    private static final int FALLBACK_TTS_DEFAULT_PITCH = 100;
    private static final int FALLBACK_TTS_DEFAULT_RATE = 100;
    private static int defaultPitch = FALLBACK_TTS_DEFAULT_PITCH, defaultRate = FALLBACK_TTS_DEFAULT_RATE;

    private static final String DEFAULT_TABS = "[true, true, true, true, false, false, false, false, false, false]";
    ////////////////////////////////////////////////
    private static final String KEY_TABS = "tabs";
    private static final String KEY_MAX_WORDS = "maxWords";
    private static final String KEY_SHOW_ADS = "showAds";
    private static final String KEY_SHOW_DIALOG = "showDialog";
    private static final String KEY_DARK_MODE = "darkMode";
    private static final String KEY_SHOW_FLOATING = "showFloating";
    private static final String KEY_SHOW_FLOATING_DIALOG = "showFloatingDialog";
    private static final String KEY_SHOW_DEFS_POPUP = "showDefsPopup";
    ////////////////////////////////////////////////
    private static final String KEY_TTS_DLG_SEEN = "ttsDialogShown";
    private static final String KEY_TTS_PITCH = "ttsPitch";
    private static final String KEY_TTS_RATE = "ttsRate";
    private static final String KEY_TTS_ENGINE = "ttsEngine";
    private static final String KEY_TTS_LANGUAGE = "ttsLanguage";
    private static final String KEY_TTS_VOICE = "ttsVoice";
    ////////////////////////////////////////////////
    private static final String KEY_FILTER_WORD = "filterWord";
    private static final String KEY_FILTER_DEFINITION = "filterDefinition";
    private static final String KEY_FILTER_CONTAINS = "filterContain";
    ////////////////////////////////////////////////
    private static SharedPreferences preferences;

    public static void setPreferences(@NonNull final Context context) {
        if (preferences == null)
            preferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE);

        final ContentResolver contentResolver = context.getContentResolver();
        defaultPitch = Settings.Secure.getInt(contentResolver, Settings.Secure.TTS_DEFAULT_PITCH,
                FALLBACK_TTS_DEFAULT_PITCH);
        defaultRate = Settings.Secure.getInt(contentResolver, Settings.Secure.TTS_DEFAULT_RATE,
                FALLBACK_TTS_DEFAULT_RATE);
    }

    public static int getNightMode() {
        return preferences != null ? preferences.getInt(KEY_DARK_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) : AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
    }

    public static int getMaxWords() {
        return preferences != null ? preferences.getInt(KEY_MAX_WORDS, 80) : 80;
    }

    public static boolean showDefsPopup() {
        return preferences == null || preferences.getBoolean(KEY_SHOW_DEFS_POPUP, true);
    }

    public static boolean showFloating() {
        return preferences == null || preferences.getBoolean(KEY_SHOW_FLOATING, true);
    }

    public static boolean showFloatingDialog() {
        return preferences == null || preferences.getBoolean(KEY_SHOW_FLOATING_DIALOG, false);
    }

    public static boolean showDialog() {
        return preferences != null && preferences.getBoolean(KEY_SHOW_DIALOG, false);
    }

    public static boolean showAds() {
        return preferences == null || preferences.getBoolean(KEY_SHOW_ADS, true);
    }

    public static boolean isFilterWords() {
        return preferences == null || preferences.getBoolean(KEY_FILTER_WORD, true);
    }

    public static boolean isFilterDefinition() {
        return preferences != null && preferences.getBoolean(KEY_FILTER_DEFINITION, false);
    }

    public static boolean isFilterContains() {
        return preferences != null && preferences.getBoolean(KEY_FILTER_CONTAINS, false);
    }

    public static boolean isTTSHelpDialogSeen() {
        return preferences != null && preferences.getBoolean(KEY_TTS_DLG_SEEN, false);
    }

    public static int getTTSPitch() {
        return preferences != null ? preferences.getInt(KEY_TTS_PITCH, defaultPitch) : defaultPitch;
    }

    @Nullable
    public static String getTTSEngine() {
        return preferences != null ? preferences.getString(KEY_TTS_ENGINE, null) : null;
    }

    @Nullable
    public static String getTTSVoice() {
        return preferences != null ? preferences.getString(KEY_TTS_VOICE, null) : null;
    }

    @Nullable
    public static String getTTSLanguage() {
        return preferences != null ? preferences.getString(KEY_TTS_LANGUAGE, null) : null;
    }

    public static int getTTSSpeechRate() {
        return preferences != null ? preferences.getInt(KEY_TTS_RATE, defaultRate) : defaultRate;
    }

    @NonNull
    public static boolean[] getTabs() {
        String bools = DEFAULT_TABS;
        if (preferences != null) {
            bools = preferences.getString("tabs", DEFAULT_TABS);
            if (Utils.isEmpty(bools)) bools = DEFAULT_TABS;
        }
        bools = bools.substring(1, bools.length() - 1);

        final boolean[] tabBoolsArray = {true, true, true, true, false, false, false, false, false, false};
        int idx = 0;
        for (int i = 0, len = bools.length(); i < len; ) {
            final char c = bools.charAt(i);
            if (c != ' ' && c != ',') {
                if (c == 't') {
                    tabBoolsArray[idx++] = true;
                    i = i + 4;
                    continue;
                }
                if (c == 'f') {
                    tabBoolsArray[idx++] = false;
                    i = i + 5;
                    continue;
                }
            }
            i = i + 1;
        }

        return tabBoolsArray;
    }

    public static void setTabs(@NonNull final String tabs) {
        if (preferences != null)
            preferences.edit().putString(KEY_TABS, !Utils.isEmpty(tabs) ? tabs : DEFAULT_TABS).apply();
    }

    public static void setFilter(@NonNull final String filterName, final boolean filtered) {
        if (preferences != null)
            preferences.edit().putBoolean(filterName, filtered).apply();
    }

    public static void setTTSPitch(final Integer pitch) {
        if (preferences != null) {
            final SharedPreferences.Editor edit = preferences.edit();
            if (pitch == null) edit.remove(KEY_TTS_PITCH);
            else edit.putInt(KEY_TTS_PITCH, pitch);
            edit.apply();
        }
    }

    public static void setTTSSpeechRate(final Integer speechRate) {
        if (preferences != null) {
            final SharedPreferences.Editor edit = preferences.edit();
            if (speechRate == null) edit.remove(KEY_TTS_RATE);
            else edit.putInt(KEY_TTS_RATE, speechRate);
            edit.apply();
        }
    }

    public static void setTTSLanguage(final String ttsLanguage) {
        if (!Utils.isEmpty(ttsLanguage) && preferences != null)
            preferences.edit().putString(KEY_TTS_LANGUAGE, ttsLanguage).apply();
    }

    public static void setTTSVoice(final String ttsVoice) {
        if (!Utils.isEmpty(ttsVoice) && preferences != null)
            preferences.edit().putString(KEY_TTS_VOICE, ttsVoice).apply();
    }

    public static void setTTSEngine(final String ttsEngine) {
        if (!Utils.isEmpty(ttsEngine) && preferences != null)
            preferences.edit().putString(KEY_TTS_ENGINE, ttsEngine).apply();
    }

    public static void setTTSHelpDialogSeen() {
        if (preferences != null)
            preferences.edit().putBoolean(KEY_TTS_DLG_SEEN, true).apply();
    }

    public static void resetTTS() {
        if (preferences != null)
            preferences.edit().remove(KEY_TTS_DLG_SEEN)
                    .remove(KEY_TTS_PITCH)
                    .remove(KEY_TTS_RATE)
                    .remove(KEY_TTS_ENGINE)
                    .remove(KEY_TTS_LANGUAGE)
                    .remove(KEY_TTS_VOICE)
                    .apply();
    }

    public static void setValues(final int maxWords, final int darkMode, final boolean showAds, final boolean showDialog,
                                 final boolean isFloating, final boolean isFloatingDialog, final boolean showDefsPopup) {
        if (preferences != null) {
            preferences.edit()
                    .putInt(KEY_MAX_WORDS, maxWords)
                    .putInt(KEY_DARK_MODE, darkMode)
                    .putBoolean(KEY_SHOW_ADS, showAds)
                    .putBoolean(KEY_SHOW_DIALOG, showDialog)
                    .putBoolean(KEY_SHOW_FLOATING, isFloating)
                    .putBoolean(KEY_SHOW_FLOATING_DIALOG, isFloatingDialog)
                    .putBoolean(KEY_SHOW_DEFS_POPUP, showDefsPopup)
                    .apply();
        }
    }
} public final class Utils {
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    private static final Intent SEARCH_QUERY_INTENT = new Intent(Intent.ACTION_WEB_SEARCH);

    public static final int[] CUSTOM_TAB_COLORS = new int[]{0xFF4888F2, 0xFF333333, 0xFF3B496B};
    public static final String CHARSET = "UTF-8";
    public static FirebaseCrashlytics firebaseCrashlytics;
    public static InputMethodManager inputMethodManager;
    public static NotificationManager notificationManager;
    public static Locale defaultLocale;
    public static int statusBarHeight = 0, navigationBarHeight = 0;
    private static ClipboardManager clipboard;

    @Nullable
    public static String getResponse(final String url) throws Exception {
        final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();

        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                final StringBuilder response = new StringBuilder();

                String currentLine;
                while ((currentLine = reader.readLine()) != null)
                    response.append(currentLine);

                return response.toString();
            }
            return null;
        } finally {
            connection.disconnect();
        }
    }

    /**
     * thanks to weston
     * https://stackoverflow.com/questions/2711858/is-it-possible-to-set-a-custom-font-for-entire-of-application/16883281#16883281
     */
    public static void setDefaultFont(final String typefaceName, final Typeface fontTypeface) {
        try {
            final Field staticField = Typeface.class.getDeclaredField(typefaceName);
            if (!staticField.isAccessible()) staticField.setAccessible(true);
            staticField.set(null, fontTypeface);
        } catch (final Exception e) {
            if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "Utils::setDefaultFont", e);
            else firebaseCrashlytics.recordException(e);
        }
    }

    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() <= 0 || cs.equals("") || isEmpty(cs.toString());
    }

    @SuppressWarnings("ConstantConditions")
    public static boolean isEmpty(String str) {
        if (str == null || str.length() <= 0 || str.isEmpty() || "null".equals(str)) return true;
        str = str.trim();
        return str.length() <= 0 || str.isEmpty() || "null".equals(str);
    }

    public static void stopAsyncSilent(final LocalAsyncTask<?, ?> localAsyncTask) {
        try {
            if (localAsyncTask != null)
                localAsyncTask.cancel(true);
        } catch (final Exception e) {
            // ignore
        }
    }

    public static void removeHandlerCallbacksSilent(final Handler handler, final Runnable runnable) {
        try {
            if (handler != null)
                handler.removeCallbacks(runnable);
        } catch (final Exception e) {
            // ignore
        }
    }

    public static void adsBox(@NonNull final Activity activity) {
        final View adLayout = activity.findViewById(R.id.adLayout);
        if (adLayout == null) return;
        if (SettingsHelper.showAds()) {
            MobileAds.initialize(activity, initializationStatus -> { });
            final AdView adView = activity.findViewById(R.id.adView);
            adView.setAdListener(new Listener(adLayout));
            adView.loadAd(new AdRequest.Builder().build());
        } else adLayout.setVisibility(View.GONE);
    }

    public static int getStatusBarHeight(final Window window, final Resources resources) {
        if (Build.VERSION.SDK_INT == 19 && window != null && resources != null) {
            final Rect rectangle = new Rect();
            window.getDecorView().getWindowVisibleDisplayFrame(rectangle);

            final View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);

            final int resId = resources.getIdentifier("status_bar_height", "dimen", "android");
            final int statusBarHeight1 = contentView.getTop() - rectangle.top;
            final int statusBarHeight2 = resId > 0 ? resources.getDimensionPixelSize(resId) : 0;

            return statusBarHeight1 == 0 && statusBarHeight2 == 0 ? 50 :
                    statusBarHeight1 == 0 ? statusBarHeight2 : statusBarHeight1;
        }
        return 0;
    }

    public static int getNavigationBarHeight(final Window window, final Resources resources) {
        if (Build.VERSION.SDK_INT == 19 && window != null && resources != null) {
            final int appUsableSizeX, appUsableSizeY, realScreenSizeX, realScreenSizeY;

            final Display display = window.getWindowManager().getDefaultDisplay();
            final Point size = new Point();
            display.getSize(size);
            appUsableSizeX = size.x;
            appUsableSizeY = size.y;

            display.getRealSize(size);
            realScreenSizeX = size.x;
            realScreenSizeY = size.y;

            final int navBarHeight1;
            if (!(appUsableSizeX < realScreenSizeX) // !(navigation bar on the right)
                    && appUsableSizeY < realScreenSizeY) // navigation bar at the bottom
                navBarHeight1 = realScreenSizeY - appUsableSizeY;
            else navBarHeight1 = 0;

            final int resId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
            final int navBarHeight2 = resId > 0 ? resources.getDimensionPixelSize(resId) : 0;

            return navBarHeight1 == 0 && navBarHeight2 == 0 ? 0 :
                    navBarHeight1 == 0 ? navBarHeight2 : navBarHeight1;
        }
        return 0;
    }

    public static void copyText(final Context context, final String stringToCopy) {
        if (clipboard == null)
            clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);

        int toastMessage = R.string.error_copying_clipboard;
        if (clipboard != null) {
            clipboard.setPrimaryClip(ClipData.newPlainText("word", stringToCopy));
            toastMessage = R.string.copied_clipboard;
        }

        Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show();
    }

    @NonNull
    public static PopupMenu setPopupMenuSlider(@NonNull final View view, @NonNull final WordItem wordItem) {
        final Context context = view.getContext();

        Object tag = view.getTag(R.id.key_popup);
        final PopupMenu popup;
        if (tag instanceof PopupMenu) popup = (PopupMenu) tag;
        else {
            popup = new PopupMenu(context, view);
            popup.getMenuInflater().inflate(R.menu.menu_word, popup.getMenu());
            view.setTag(R.id.key_popup, popup);
        }

        tag = view.getTag(R.id.key_popup_item_listener);
        final WordContextItemListener itemListener;
        if (tag instanceof WordContextItemListener) itemListener = (WordContextItemListener) tag;
        else {
            itemListener = new WordContextItemListener(context);
            view.setTag(R.id.key_popup_item_listener, itemListener);
        }

        itemListener.setWord(wordItem.getWord());

        popup.setOnMenuItemClickListener(itemListener);
        view.setOnTouchListener(popup.getDragToOpenListener());

        return popup;
    }

    public static void showPopupMenu(@NonNull final View view, @NonNull final WordItem wordItem) {
        setPopupMenuSlider(view, wordItem).show();
    }

    @NonNull
    public static PopupMenu setPopupMenuSlider(final Dialog dialog, final Context context, @NonNull final View view, final String word) {
        final PopupMenu popup = new PopupMenu(context, view);
        final Menu menu = popup.getMenu();
        popup.getMenuInflater().inflate(R.menu.menu_search, menu);

        for (int i = tabBoolsArray.length - 1; i >= 0; i--)
            menu.getItem(i).setVisible(tabBoolsArray[i]);

        popup.setOnMenuItemClickListener(new WordClickSearchListener(context, dialog, word));

        view.setOnTouchListener(popup.getDragToOpenListener());
        view.setTag(R.id.key_popup, popup);

        return popup;
    }

    public static void showPopupMenu(final Dialog dialog, final Context context, final View view, final String word) {
        if (context != null && view != null && !isEmpty(word)) {
            final Object tag = view.getTag(R.id.key_popup);
            final PopupMenu popup = tag instanceof PopupMenu ? (PopupMenu) tag :
                    setPopupMenuSlider(dialog, context, view, word);
            popup.show();
        }
    }

    public static void speakText(final CharSequence text) {
        if (tts != null && !isEmpty(text)) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
            else
                tts.speak(String.valueOf(text), TextToSpeech.QUEUE_FLUSH, null); // todo change deprecated
        }
    }

    public static float distance(@NonNull final PointF pointF, @NonNull final PointF other) {
        return (float) sqrt((other.y - pointF.y) * (other.y - pointF.y) + (other.x - pointF.x) * (other.x - pointF.x));
    }

    @NonNull
    public static ContextThemeWrapper getStyledContext(final Context context, @StyleRes int style) {
        if (context instanceof ContextThemeWrapper) return (ContextThemeWrapper) context;
        if (style == 0 || style == -1) {
            final Resources.Theme theme;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && (theme = context.getTheme()) != null)
                return new ContextThemeWrapper(context, theme);
            else {
                final Context appContext = context.getApplicationContext();

                final PackageManager packageManager = appContext.getPackageManager();
                final String packageName = appContext.getPackageName();

                int styleHack;
                try {
                    final PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
                    styleHack = packageInfo.applicationInfo.theme;
                    Log.d("AWAISKING_APP", "styleHack1: " + styleHack);
                } catch (final Exception e) {
                    Log.e("AWAISKING_APP", "err1", e);
                    styleHack = -1;
                }

                if (styleHack == 0 || styleHack == -1) {
                    try {
                        final Intent intent = packageManager.getLaunchIntentForPackage(packageName);
                        //noinspection ConstantConditions
                        final ActivityInfo activityInfo = packageManager.getActivityInfo(intent.getComponent(), 0);
                        styleHack = activityInfo.getThemeResource();
                        Log.d("AWAISKING_APP", "styleHack2: " + styleHack);
                    } catch (final Exception e) {
                        Log.e("AWAISKING_APP", "err2", e);
                        styleHack = -1;
                    }
                }

                //try {
                //    final ActivityInfo activityInfo = packageManager.getActivityInfo(new ComponentName(appContext, Main.class), 0);
                //    styleHack = activityInfo.getThemeResource();
                //} catch (final Throwable e) {
                //    styleHack = -1;
                //}
                //if (styleHack == 0 || styleHack == -1) {
                //    try {
                //        final ActivityInfo activityInfo = packageManager.getActivityInfo(new ComponentName(context, Main.class), 0);
                //        styleHack = activityInfo.getThemeResource();
                //    } catch (final Throwable e) {
                //        styleHack = -1;
                //    }
                //}

                if (styleHack != 0 && styleHack != -1) style = styleHack;
            }
        }
        return new ContextThemeWrapper(context, style);
    }

    public static boolean isAnySearchAppFound(@NonNull final Context context) {
        final String appID = context.getApplicationContext().getPackageName();
        final PackageManager packageManager = context.getPackageManager();

        final List<ResolveInfo> pkgAppsList = packageManager.queryIntentActivities(SEARCH_QUERY_INTENT, 0);
        int pkgsSize = pkgAppsList.size();
        for (int i = pkgsSize - 1; i >= 0; i--) {
            final ResolveInfo resolveInfo = pkgAppsList.get(i);
            final String strResolveInfo = String.valueOf(resolveInfo);

            if (strResolveInfo.contains(appID) && strResolveInfo.contains("TextProcessHelper") ||
                    resolveInfo != null && resolveInfo.activityInfo != null && appID.equals(resolveInfo.activityInfo.packageName))
                --pkgsSize;
        }

        return pkgsSize > 0;
    }

    // extracted from String class
    public static int indexOfChar(@NonNull final CharSequence sequence, final int ch, final int startIndex) {
        final int max = sequence.length();
        if (startIndex < max) {
            if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
                for (int i = startIndex; i < max; i++) if (sequence.charAt(i) == ch) return i;
            } else if (Character.isValidCodePoint(ch)) {
                final char hi = (char) ((ch >>> 10) + (Character.MIN_HIGH_SURROGATE - (Character.MIN_SUPPLEMENTARY_CODE_POINT >>> 10)));
                final char lo = (char) ((ch & 0x3ff) + MIN_LOW_SURROGATE);
                for (int i = startIndex; i < max; i++)
                    if (sequence.charAt(i) == hi && sequence.charAt(i + 1) == lo) return i;
            }
        }
        return -1;
    }
} public final class MyApps {
    /**
     * copy paste this shit
     * <p>
     * MyApps.showAlertDialog(this, (parent, view, position, id) -> {
     * if (id == -1 && position == -1 && parent == null) super.onBackPressed();
     * else MyApps.openAppStore(this, position);
     * });
     */
    private final static Icons[] iconsList;

    static {
        final Icons[] currList = new Icons[]{
                new Icons("awais.addme", "AddMe", R.drawable.adm),
                new Icons("awais.quodb", "QuoDB", R.drawable.qdb),
                new Icons("awais.media.scanner", "mediaScanner", R.drawable.ms),
                new Icons("awais.skyrimconsole", "Skyrim Cheats", R.drawable.tesv),
                new Icons("awais.reversify", "Reversify", R.drawable.rev),
                new Icons("awais.reversify.lite", "Reversify Lite", R.drawable.revl),
                new Icons("awais.backworddictionary", "Linked Words", R.drawable.lw),
                new Icons("awais.game.tictactoe", "Tic Tac Toe", R.drawable.ttt),
                new Icons("awais.game.jigsaw", "JigSaw", R.drawable.jsw),
        };
        iconsList = new Icons[currList.length - 1];
        int i = 0;
        for (final Icons value : currList) if (!value.pkg.equals(BuildConfig.APPLICATION_ID)) iconsList[i++] = value;
    }

    public static void openAppStore(@NonNull final Context context, final int position) {
        try {
            context.startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse("https://play.google.com/store/apps/details?id=" + MyApps.iconsList[position].pkg)));
        } catch (final Exception e) {
            // activity not found? bruh
        }
    }

    public static void showAlertDialog(final Context context, final AdapterView.OnItemClickListener clickListener) {
        final DialogInterface.OnCancelListener cancelListener = d -> {
            if (clickListener != null) clickListener.onItemClick(null, null, -1, -1);
            else {
                if (context instanceof Activity) ((Activity) context).finish();
                else Process.killProcess(Process.myPid());
            }
        };
        if (new Random().nextDouble() < 0.6D) {
            cancelListener.onCancel(null);
            return;
        }
        final GridView gridView = new GridView(context);
        gridView.setAdapter(new ImageAdapter(context));
        gridView.setNumColumns(3);
        gridView.setOnItemClickListener(clickListener);
        final AlertDialog dialog = new MaterialAlertDialogBuilder(context, R.style.MaterialAlertDialogTheme)
                .setView(gridView).setTitle("Support my apps").create();
        dialog.setOnCancelListener(cancelListener);
        dialog.show();
    }

    private static class Icons {
        private final int icon;
        private final String title, pkg;

        private Icons(final String pkg, final String title, final int icon) {
            this.title = title;
            this.pkg = pkg;
            this.icon = icon;
        }
    }

    private static class ImageAdapter extends BaseAdapter {
        private final Context context;
        private final int size;

        public ImageAdapter(@NonNull final Context context) {
            this.context = context;
            this.size = (int) (80 * context.getResources().getDisplayMetrics().density);
        }

        @Override
        public int getCount() {
            return iconsList.length;
        }

        @Override
        public Object getItem(final int position) {
            return iconsList[position];
        }

        @Override
        public long getItemId(final int position) {
            return 0;
        }

        @NonNull
        @Override
        public View getView(final int position, View convertView, final ViewGroup parent) {
            final ViewHolder holder;
            if (convertView == null) {
                final LinearLayout linearLayout = new LinearLayout(context);
                linearLayout.setOrientation(LinearLayout.VERTICAL);
                final AppCompatImageView imageView = new AppCompatImageView(context);
                final MaterialTextView textView = new MaterialTextView(context);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                    textView.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
                textView.setGravity(Gravity.CENTER_HORIZONTAL);
                imageView.setAdjustViewBounds(true);
                linearLayout.addView(imageView, LinearLayout.LayoutParams.MATCH_PARENT, size);
                linearLayout.addView(textView);
                final int padding = size / 4;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                    linearLayout.setPaddingRelative(padding, padding, padding, padding);
                else linearLayout.setPadding(padding, padding, padding, padding);
                convertView = linearLayout;
                convertView.setTag(holder = new ViewHolder(textView, imageView));
            } else
                holder = (ViewHolder) convertView.getTag();

            final Object item = getItem(position);
            if (item instanceof Icons) {
                final Icons icons = (Icons) item;
                holder.title.setText(icons.title);
                holder.icon.setImageResource(icons.icon);
            }
            return convertView;
        }

        private final static class ViewHolder {
            private final TextView title;
            private final ImageView icon;

            private ViewHolder(final TextView title, final ImageView icon) {
                this.title = title;
                this.icon = icon;
            }
        }
    }
} public class AdapterClickListener implements View.OnClickListener, View.OnLongClickListener {
    @Override
    public void onClick(final View view) { }

    @Override
    public boolean onLongClick(final View view) {
        return view != null && BuildConfig.DEBUG;
    }
} public interface FragmentLoader {
    void loadFragments(final boolean main);
} public interface MainCheck {
    void afterSearch(final ArrayList<WordItem> result);
} public interface TTSRefresher {
    void doRefresh();
} public final class MaterialSearchView extends FrameLayout implements View.OnClickListener {
    public static final int SPEECH_REQUEST_CODE = 4000;
    public static int iconColor = Color.BLACK;
    private final Context context;
    private SearchArrowDrawable searchArrow = null;
    private RecyclerView.Adapter<?> adapter = null;
    private OnQueryTextListener onQueryChangeListener = null;
    private OnOpenCloseListener onOpenCloseListener = null;

    private SearchViewBinding searchViewBinding;

    private View menuItemView = null;
    private CharSequence oldQueryText, userQuery = "";
    private int menuItemCx = -1;
    private boolean isSearchOpen = false, isVoice = false, isHandlingIntentData = false;

    public MaterialSearchView(final Context context) {
        this(context, null);
    }

    public MaterialSearchView(final Context context, @Nullable final AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MaterialSearchView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        if (Utils.inputMethodManager == null)
            Utils.inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        initView();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MaterialSearchView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.context = context;
        if (Utils.inputMethodManager == null)
            Utils.inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        initView();
    }

    private void initView() {
        LayoutInflater.from(context).inflate(R.layout.search_view, this, true);
        searchViewBinding = SearchViewBinding.bind(this);

        searchViewBinding.rvResults.setNestedScrollingEnabled(false);
        searchViewBinding.rvResults.setLayoutManager(new LinearLayoutManager(context));
        searchViewBinding.rvResults.setItemAnimator(null);
        searchViewBinding.rvResults.setVisibility(View.GONE);

        searchViewBinding.viewDivider.setVisibility(View.GONE);
        searchViewBinding.viewShadow.setVisibility(View.GONE);

        searchViewBinding.viewShadow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.shadow_color, null));
        searchViewBinding.viewShadow.setOnClickListener(this);

        searchViewBinding.progressBar.setVisibility(View.GONE);

        searchViewBinding.ivMic.setOnClickListener(this);
        searchViewBinding.ivMic.setVisibility(View.GONE);

        searchViewBinding.btnCancel.setOnClickListener(this);
        searchViewBinding.btnCancel.setVisibility(View.GONE);

        searchViewBinding.etSearchView.setSearchView(this);
        searchViewBinding.etSearchView.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(final Editable editable) { }

            @Override
            public void beforeTextChanged(final CharSequence charSequence, final int start, final int count, final int after) { }

            @Override
            public void onTextChanged(final CharSequence charSequence, final int start, final int before, final int count) {
                if (!isHandlingIntentData) onSearchTextChanged(charSequence);
            }
        });
        searchViewBinding.etSearchView.setOnEditorActionListener((textView, i, keyEvent) -> {
            onSubmitQuery();
            return true;
        });
        searchViewBinding.etSearchView.setOnFocusChangeListener((v, hasFocus) -> {
            if (!Utils.isEmpty(userQuery)) {
                searchViewBinding.btnCancel.setVisibility(hasFocus ? View.VISIBLE : View.GONE);
                if (isVoice) searchViewBinding.ivMic.setVisibility(hasFocus ? View.GONE : VISIBLE);
            }

            if (hasFocus) addFocus();
            else removeFocus();
        });

        setVisibility(View.GONE);

        searchArrow = new SearchArrowDrawable(new ContextThemeWrapper(context, context.getTheme()));
        searchViewBinding.ivBack.setImageDrawable(searchArrow);
        searchViewBinding.ivBack.setOnClickListener(this);

        final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);

        final Resources resources = context.getResources();
        final int top = resources.getDimensionPixelSize(R.dimen.search_menu_item_margin);
        final int leftRight = resources.getDimensionPixelSize(R.dimen.search_menu_item_margin_left_right);
        final int bottom = resources.getDimensionPixelSize(R.dimen.search_menu_item_margin);

        params.setMargins(leftRight, top, leftRight, bottom);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            params.setMarginEnd(leftRight);
            params.setMarginStart(leftRight);
        }

        searchViewBinding.cardView.setLayoutParams(params);

        setBackgroundColor(ContextCompat.getColor(context, R.color.search_background));

        // setIconColor
        iconColor = ContextCompat.getColor(context, R.color.search_icon);
        final ColorFilter colorFilter = new PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
        searchViewBinding.ivBack.setColorFilter(colorFilter);
        searchViewBinding.ivMic.setColorFilter(colorFilter);
        searchViewBinding.btnCancel.setColorFilter(colorFilter);

        isVoice = isVoiceAvailable();

        if (isVoice) searchViewBinding.etSearchView.setPrivateImeOptions("nm");
        searchViewBinding.ivMic.setVisibility(isVoice ? View.VISIBLE : View.GONE);
    }

    public void setHandlingIntentData(final boolean isHandlingIntentData) {
        this.isHandlingIntentData = isHandlingIntentData;
    }

    public void setQuery(final CharSequence query, final boolean submit) {
        setQueryWithoutSubmitting(query);

        if (!Utils.isEmpty(userQuery)) {
            searchViewBinding.btnCancel.setVisibility(View.GONE);
            if (isVoice) searchViewBinding.ivMic.setVisibility(View.VISIBLE);
        }

        if (submit && !Utils.isEmpty(query)) onSubmitQuery();
    }

    public void setAdapter(final RecyclerView.Adapter<?> adapter) {
        this.adapter = adapter;
        searchViewBinding.rvResults.setAdapter(this.adapter);
    }

    @Override
    public void setBackgroundColor(@ColorInt final int color) {
        searchViewBinding.cardView.setCardBackgroundColor(color);
    }

    @Override
    public void setElevation(final float elevation) {
        searchViewBinding.cardView.setMaxCardElevation(elevation);
        searchViewBinding.cardView.setCardElevation(elevation);
        invalidate();
    }

    public void open(final boolean animate, final MenuItem menuItem) {
        setVisibility(View.VISIBLE);

        if (animate) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                if (menuItem != null) getMenuItemPosition(menuItem.getItemId());
                searchViewBinding.cardView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        searchViewBinding.cardView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        SearchAnimator.revealOpen(searchViewBinding.cardView, menuItemCx, context,
                                searchViewBinding.etSearchView, onOpenCloseListener);
                    }
                });
            } else
                SearchAnimator.fadeOpen(searchViewBinding.cardView, searchViewBinding.etSearchView, onOpenCloseListener);
        } else {
            searchViewBinding.cardView.setVisibility(View.VISIBLE);
            if (onOpenCloseListener != null) onOpenCloseListener.onOpen();
            searchViewBinding.etSearchView.requestFocus();
        }
    }

    public void close(final boolean animate) {
        if (animate) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                SearchAnimator.revealClose(searchViewBinding.cardView, menuItemCx, context,
                        searchViewBinding.etSearchView, this, onOpenCloseListener);
            else
                SearchAnimator.fadeClose(searchViewBinding.cardView, searchViewBinding.etSearchView,
                        this, onOpenCloseListener);
        } else {
            searchViewBinding.etSearchView.clearFocus();
            searchViewBinding.cardView.setVisibility(View.GONE);
            setVisibility(View.GONE);
            if (onOpenCloseListener != null) onOpenCloseListener.onClose();
        }
    }

    private void addFocus() {
        isSearchOpen = true;
        setArrow();
        SearchAnimator.fadeIn(searchViewBinding.viewShadow);
        showSuggestions();
        showKeyboard();
    }

    private void removeFocus() {
        isSearchOpen = false;
        setHamburger();
        SearchAnimator.fadeOut(searchViewBinding.viewShadow);
        hideSuggestions();
        hideKeyboard();
    }

    public void showSuggestions() {
        if (adapter != null && adapter.getItemCount() > 0) searchViewBinding.viewDivider.setVisibility(View.VISIBLE);
        searchViewBinding.rvResults.setVisibility(View.VISIBLE);
        SearchAnimator.fadeIn(searchViewBinding.rvResults);
    }

    private void hideSuggestions() {
        searchViewBinding.viewDivider.setVisibility(View.GONE);
        searchViewBinding.rvResults.setVisibility(View.GONE);
        SearchAnimator.fadeOut(searchViewBinding.rvResults);
    }

    public boolean isSearchOpen() {
        return isSearchOpen; // getVisibility();
    }

    private void showKeyboard() {
        if (!isInEditMode() && Utils.inputMethodManager != null) {
            Utils.inputMethodManager.showSoftInput(searchViewBinding.etSearchView, 0);
            Utils.inputMethodManager.showSoftInput(this, 0);
        }
    }

    private void hideKeyboard() {
        if (!isInEditMode() && Utils.inputMethodManager != null)
            Utils.inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
    }

    public void showProgress() {
        searchViewBinding.progressBar.setVisibility(View.VISIBLE);
    }

    public void hideProgress() {
        searchViewBinding.progressBar.setVisibility(View.GONE);
    }

    public boolean isShowingProgress() {
        return searchViewBinding.progressBar.getVisibility() == View.VISIBLE;
    }

    private void setQueryWithoutSubmitting(final CharSequence query) {
        searchViewBinding.etSearchView.setText(query);
        if (query != null) {
            searchViewBinding.etSearchView.setSelection(searchViewBinding.etSearchView.length());
            userQuery = query;
        } else if (searchViewBinding.etSearchView.getText() != null)
            searchViewBinding.etSearchView.getText().clear();
    }

    private void setArrow() {
        if (searchArrow != null) {
            searchArrow.setVerticalMirror(false);
            searchArrow.animate(SearchArrowDrawable.STATE_ARROW);
        }
    }

    private void setHamburger() {
        if (searchArrow != null) {
            searchArrow.setVerticalMirror(true);
            searchArrow.animate(SearchArrowDrawable.STATE_HAMBURGER);
        }
    }

    private void getMenuItemPosition(final int menuItemId) {
        if (menuItemView != null) menuItemCx = getCenterX(menuItemView);
        ViewParent viewParent = getParent();
        while (viewParent instanceof View) {
            final View parent = (View) viewParent;
            final View view = parent.findViewById(menuItemId);
            if (view != null) {
                menuItemView = view;
                menuItemCx = getCenterX(menuItemView);
                break;
            }
            viewParent = viewParent.getParent();
        }
    }

    private void onVoiceClicked() {
        if (context instanceof Activity) {
            try {
                final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
                intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
                intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak now");
                intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);

                ((Activity) context).startActivityForResult(intent, SPEECH_REQUEST_CODE);
            } catch (final ActivityNotFoundException e) {
                Toast.makeText(context, "No app or service found to perform voice search.", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private boolean isVoiceAvailable() {
        if (isInEditMode()) return true;
        final PackageManager pm = context.getPackageManager();
        final List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
        return activities.size() != 0;
    }

    private void onSubmitQuery() {
        final CharSequence query = searchViewBinding.etSearchView.getText();
        if (query != null && TextUtils.getTrimmedLength(query) > 0 &&
                (onQueryChangeListener == null || !onQueryChangeListener.onQueryTextSubmit(query.toString())))
            searchViewBinding.etSearchView.setText(query);
    }

    private void onSearchTextChanged(final CharSequence newText) {
        userQuery = searchViewBinding.etSearchView.getText();

        if (adapter instanceof Filterable) ((Filterable) adapter).getFilter().filter(userQuery);

        if (onQueryChangeListener != null && !TextUtils.equals(newText, oldQueryText))
            onQueryChangeListener.onQueryTextChange(newText.toString());
        oldQueryText = newText.toString();

        final boolean isTextEmpty = Utils.isEmpty(newText);
        searchViewBinding.btnCancel.setVisibility(isTextEmpty ? View.GONE : View.VISIBLE);
        if (isVoice) searchViewBinding.ivMic.setVisibility(isTextEmpty ? View.VISIBLE : View.GONE);
    }

    private int getCenterX(@NonNull final View view) {
        final int[] location = new int[2];
        view.getLocationOnScreen(location);
        return location[0] + (view.getWidth() >>> 1);
    }

    @Override
    public void onClick(final View v) {
        if (v == searchViewBinding.ivMic)
            onVoiceClicked();
        else if (v == searchViewBinding.viewShadow || v == searchViewBinding.ivBack)
            close(true);
        else if (v == searchViewBinding.btnCancel && searchViewBinding.etSearchView.length() > 0) {
            final Editable text = searchViewBinding.etSearchView.getText();
            if (text != null) text.clear();
        }
    }

    @NonNull
    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();
        final SavedState ss = new SavedState(superState);

        ss.query = userQuery != null ? userQuery.toString() : null;
        ss.isSearchOpen = isSearchOpen;

        return ss;
    }

    @Override
    protected void onRestoreInstanceState(final Parcelable state) {
        if (state instanceof SavedState) {
            final SavedState ss = (SavedState) state;
            if (ss.isSearchOpen) {
                open(true, null);
                setQueryWithoutSubmitting(ss.query);
                searchViewBinding.etSearchView.requestFocus();
            }

            super.onRestoreInstanceState(ss.getSuperState());
            requestLayout();
        } else {
            super.onRestoreInstanceState(state);
        }
    }

    public void setOnQueryTextListener(final OnQueryTextListener listener) {
        onQueryChangeListener = listener;
    }

    public void setOnOpenCloseListener(final OnOpenCloseListener listener) {
        onOpenCloseListener = listener;
    }

    public interface OnOpenCloseListener {
        void onClose();
        void onOpen();
    }

    private static class SavedState extends BaseSavedState {
        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
            @NonNull
            @Override
            public SavedState createFromParcel(final Parcel in) {
                return new SavedState(in);
            }

            @NonNull
            @Override
            public SavedState[] newArray(final int size) {
                return new SavedState[size];
            }
        };
        private String query;
        private boolean isSearchOpen;

        SavedState(final Parcelable superState) {
            super(superState);
        }

        SavedState(final Parcel source) {
            super(source);
            this.query = source.readString();
            this.isSearchOpen = source.readInt() == 1;
        }

        @Override
        public void writeToParcel(final Parcel out, final int flags) {
            super.writeToParcel(out, flags);
            out.writeString(query);
            out.writeInt(isSearchOpen ? 1 : 0);
        }
    }
} public final class SearchItem implements Parcelable {
    public static final Creator<SearchItem> CREATOR = new Creator<SearchItem>() {
        @Override
        @NonNull
        public SearchItem createFromParcel(final Parcel source) {
            return new SearchItem(source);
        }

        @Override
        @NonNull
        public SearchItem[] newArray(final int size) {
            return new SearchItem[size];
        }
    };
    private final int icon;
    private final boolean searchable;
    private final String text;

    public SearchItem(final String text) {
        this(R.drawable.ic_search, text, true);
    }

    public SearchItem(final int icon, final String text, final boolean searchable) {
        this.icon = icon;
        this.text = text;
        this.searchable = searchable;
    }

    private SearchItem(@NonNull final Parcel in) {
        this.icon = in.readInt();
        this.text = in.readString();
        this.searchable = in.readInt() == 1;
    }

    public int getIcon() {
        return this.icon;
    }

    public String getText() {
        return this.text;
    }

    public boolean isSearchable() {
        return searchable;
    }

    @Override
    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
        dest.writeInt(this.icon);
        dest.writeString(this.text);
        dest.writeInt(this.searchable ? 1 : 0);
    }

    @Override
    public boolean equals(final Object o) {
        if (o == this) return true;
        if (o == null || getClass() != o.getClass()) return false;
        final SearchItem item = (SearchItem) o;
        return icon == item.icon && searchable == item.searchable && text.equals(item.text);
    }

    @Override
    public int hashCode() {
        return getHashCode(icon, searchable, text);
    }

    @NonNull
    @Override
    public String toString() {
        return text;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    private static int getHashCode(final Object... values) {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Objects.hash(values) : Arrays.hashCode(values);
    }
}
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.TooltipCompat;
import androidx.core.content.ContextCompat;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import awais.backworddictionary.DictionaryFragment.FilterMethod;
import awais.backworddictionary.adapters.DictionaryFragmentsAdapter;
import awais.backworddictionary.adapters.SearchAdapter;
import awais.backworddictionary.adapters.holders.WordItem;
import awais.backworddictionary.asyncs.SearchAsyncTask;
import awais.backworddictionary.custom.SearchHistoryTable;
import awais.backworddictionary.databinding.ActivityMainBinding;
import awais.backworddictionary.dialogs.AdvancedDialog;
import awais.backworddictionary.dialogs.SettingsDialog;
import awais.backworddictionary.helpers.BubbleHelper;
import awais.backworddictionary.helpers.MenuHelper;
import awais.backworddictionary.helpers.SettingsHelper;
import awais.backworddictionary.helpers.Utils;
import awais.backworddictionary.helpers.other.MyApps;
import awais.backworddictionary.interfaces.AdapterClickListener;
import awais.backworddictionary.interfaces.FragmentLoader;
import awais.backworddictionary.interfaces.MainCheck;
import awais.backworddictionary.interfaces.TTSRefresher;
import awais.lapism.MaterialSearchView;
import awais.lapism.SearchItem;
package awais.backworddictionary;






public final class Main extends AppCompatActivity implements FragmentLoader, MainCheck {
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    public static final int TTS_DATA_CHECK_CODE = 775;
    static final TTSRefresher ttsRefresher = () -> onTTSInit(TextToSpeech.SUCCESS);
    private static final int[] tabs = {R.string.reverse, R.string.sounds_like, R.string.spelled_like, R.string.synonyms, R.string.antonyms,
            R.string.triggers, R.string.part_of, R.string.comprises, R.string.rhymes, R.string.homophones};
    public static boolean[] tabBoolsArray = {true, true, true, true, false, false, false, false, false, false};
    public static boolean isTTSAsyncRunning;
    public static TextToSpeech tts;
    private boolean isBubbling = false;
    private MenuHelper menuHelper;
    private AlertDialog ttsErrorDialog;
    private SearchAdapter searchAdapter;
    private SettingsDialog settingsDialog;
    private SearchHistoryTable historyDatabase;
    private AppBarLayout.LayoutParams toolbarParams;
    public DictionaryFragmentsAdapter fragmentsAdapter;
    public ActivityMainBinding mainBinding;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Window window = getWindow();
        final Resources resources = getResources();

        Utils.statusBarHeight = Utils.getStatusBarHeight(window, resources);
        Utils.navigationBarHeight = Utils.getNavigationBarHeight(window, resources);
        Utils.inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        Utils.defaultLocale = Locale.getDefault();
        final int nightMode = SettingsHelper.getNightMode();
        if (nightMode != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
            AppCompatDelegate.setDefaultNightMode(nightMode);

        final View contentView = View.inflate(this, R.layout.activity_main, null);
        mainBinding = ActivityMainBinding.bind(contentView);
        setContentView(contentView);

        final View decorView = window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
        if (Build.VERSION.SDK_INT == 19 && Utils.statusBarHeight > 0 && decorView instanceof ViewGroup) {
            final ViewGroup viewGroup = (ViewGroup) decorView;

            boolean isFound = false;
            View child;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                child = viewGroup.getChildAt(i);
                if (child != null && child.getId() == android.R.id.custom) {
                    isFound = true;
                    break;
                }
            }

            if (!isFound) {
                final Context context = viewGroup.getContext();
                final View view = new View(context, null, 0);
                view.setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimary));
                view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        Utils.statusBarHeight));
                view.setFocusableInTouchMode(false);
                view.setLongClickable(true);
                view.setFocusable(false);
                view.setClickable(true);
                viewGroup.addView(view);
            }
        }

        menuHelper = new MenuHelper(this);
        Utils.adsBox(this);

        toolbarParams = (AppBarLayout.LayoutParams) mainBinding.toolbar.getLayoutParams();
        mainBinding.appbarLayout.setPadding(0, Utils.statusBarHeight, 0, 0);
        contentView.setPadding(0, 0, 0, Utils.navigationBarHeight);

        setSupportActionBar(mainBinding.toolbar);

        // check for tts before initializing it
        try {
            startActivityForResult(new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA),
                    TTS_DATA_CHECK_CODE);
        } catch (final Throwable e) {
            // tts check activity not found
            Toast.makeText(Main.this, R.string.tts_act_not_found, Toast.LENGTH_SHORT).show();
        }

        final ViewGroup.LayoutParams fabParams = mainBinding.fabOptions.getLayoutParams();
        mainBinding.fabOptions.setLongClickListener(v -> {
            if (fragmentsAdapter == null || mainBinding == null) return true;
            final DictionaryFragment fragment = fragmentsAdapter.getItem(mainBinding.viewPager.getCurrentItem());
            if (fragment.isAdded())
                if (fragment.isFilterOpen()) fragment.hideFilter();
                else fragment.showFilter(true, FilterMethod.RECYCLER_PADDING);