package eu.faircode.email; /* This file is part of FairEmail. FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FairEmail. If not, see . Copyright 2018-2020 by Marcel Bokhorst (M66B) */ import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.database.MatrixCursor; import android.os.Bundle; import android.text.TextUtils; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.constraintlayout.widget.Group; import androidx.cursoradapter.widget.CursorAdapter; import androidx.cursoradapter.widget.SimpleCursorAdapter; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.lifecycle.Lifecycle; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; import androidx.viewpager.widget.ViewPager; import com.google.android.material.tabs.TabLayout; public class FragmentOptions extends FragmentBase { private ViewPager pager; private PagerAdapter adapter; private String searching = null; private static int[] TAB_PAGES = { R.layout.fragment_setup, R.layout.fragment_options_synchronize, R.layout.fragment_options_send, R.layout.fragment_options_connection, R.layout.fragment_options_display, R.layout.fragment_options_behavior, R.layout.fragment_options_privacy, R.layout.fragment_options_encryption, R.layout.fragment_options_notifications, R.layout.fragment_options_misc }; static String[] OPTIONS_RESTART = new String[]{ "first", "app_support", "notify_archive", "message_swipe", "message_select", "folder_actions", "folder_sync", "subscriptions", "portrait2", "landscape", "landscape3", "startup", "cards", "beige", "indentation", "date", "threading", "threading_unread", "highlight_unread", "color_stripe", "avatars", "gravatars", "favicons", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold", "name_email", "prefer_contact", "distinguish_contacts", "show_recipients", "authentication", "subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize", "keywords_header", "labels_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines", "message_zoom", "overview_mode", "addresses", "attachments_alt", "thumbnails", "contrast", "monospaced", "text_color", "text_size", "text_font", "text_align", "text_separators", "inline_images", "collapse_quotes", "seekbar", "actionbar", "actionbar_color", "navbar_colorize", "autoscroll", "swipenav", "swipe_close", "swipe_move", "autoexpand", "autoclose", "onclose", "language_detection", "quick_filter", "quick_scroll", "experiments", "debug", "biometrics" }; @Override public void onSaveInstanceState(Bundle outState) { outState.putString("fair:searching", searching); super.onSaveInstanceState(outState); } @Override @Nullable public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_options, container, false); setHasOptionsMenu(true); if (savedInstanceState != null) searching = savedInstanceState.getString("fair:searching"); pager = view.findViewById(R.id.pager); adapter = new PagerAdapter(getChildFragmentManager()); pager.setAdapter(adapter); pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // Do nothing } @Override public void onPageSelected(int position) { if (position > 0) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); boolean setup_advanced = prefs.getBoolean("setup_advanced", false); if (!setup_advanced) { prefs.edit().putBoolean("setup_advanced", true).apply(); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); lbm.sendBroadcast(new Intent(ActivitySetup.ACTION_SETUP_ADVANCED)); } } } @Override public void onPageScrollStateChanged(int state) { // Do nothing } }); addKeyPressedListener(new ActivityBase.IKeyPressedListener() { @Override public boolean onKeyPressed(KeyEvent event) { return false; } @Override public boolean onBackPressed() { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { onExit(); return true; } else return false; } }); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(pager); String tab = getActivity().getIntent().getStringExtra("tab"); if ("connection".equals(tab)) pager.setCurrentItem(3); else if ("display".equals(tab)) pager.setCurrentItem(4); else if ("encryption".equals(tab)) pager.setCurrentItem(7); getActivity().getIntent().removeExtra("tab"); } @Override protected void finish() { onExit(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_setup, menu); final String saved = searching; final MenuItem menuSearch = menu.findItem(R.id.menu_search); final SearchView searchView = (SearchView) menuSearch.getActionView(); searchView.setQueryHint(getString(R.string.title_search)); final SearchView.OnSuggestionListener onSuggestionListener = new SearchView.OnSuggestionListener() { @Override public boolean onSuggestionSelect(int position) { return false; } @Override public boolean onSuggestionClick(int position) { Cursor cursor = searchView.getSuggestionsAdapter().getCursor(); if (cursor != null && cursor.moveToPosition(position)) { int tab = cursor.getInt(cursor.getColumnIndex("tab")); int resid = cursor.getInt(cursor.getColumnIndex("resid")); pager.setCurrentItem(tab); FragmentBase fragment = (FragmentBase) adapter.instantiateItem(pager, tab); fragment.scrollTo(resid); menuSearch.collapseActionView(); } return true; } }; searchView.setOnSuggestionListener(onSuggestionListener); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { private String[] titles = null; private View[] views = null; @Override public boolean onQueryTextSubmit(String query) { searching = query; CursorAdapter adapter = searchView.getSuggestionsAdapter(); if (adapter != null && adapter.getCount() > 0) onSuggestionListener.onSuggestionClick(0); return false; } @Override public boolean onQueryTextChange(String newText) { searching = newText; suggest(newText); return false; } private void suggest(String query) { MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "tab", "resid", "title"}); if (query != null && query.length() > 1) { if (titles == null || views == null) { titles = new String[TAB_PAGES.length]; views = new View[TAB_PAGES.length]; LayoutInflater inflater = LayoutInflater.from(searchView.getContext()); for (int tab = 0; tab < TAB_PAGES.length; tab++) { titles[tab] = (String) adapter.getPageTitle(tab); views[tab] = inflater.inflate(TAB_PAGES[tab], null); } } int id = 0; for (int tab = 0; tab < TAB_PAGES.length; tab++) id = getSuggestions(query.toLowerCase(), id, tab, titles[tab], views[tab], cursor); } searchView.setSuggestionsAdapter(new SimpleCursorAdapter( searchView.getContext(), R.layout.spinner_item1_dropdown, cursor, new String[]{"title"}, new int[]{android.R.id.text1}, 0 )); } private int getSuggestions(String query, int id, int tab, String title, View view, MatrixCursor cursor) { if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; for (int i = 0; i <= group.getChildCount(); i++) id = getSuggestions(query, id, tab, title, group.getChildAt(i), cursor); } else if (view instanceof TextView) { String description = ((TextView) view).getText().toString(); if (description.toLowerCase().contains(query)) { String text = view.getContext().getString(R.string.title_title_description, title, description); cursor.newRow() .add(id++) .add(tab) .add(view.getId()) .add(text); } } return id; } }); if (!TextUtils.isEmpty(saved)) { menuSearch.expandActionView(); searchView.setQuery(saved, false); } super.onCreateOptionsMenu(menu, inflater); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); try { switch (requestCode) { case ActivitySetup.REQUEST_STILL: if (resultCode == Activity.RESULT_OK) pager.setCurrentItem(0); else super.finish(); break; } } catch (Throwable ex) { Log.e(ex); } } private void onExit() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); boolean setup_reminder = prefs.getBoolean("setup_reminder", true); boolean hasPermissions = hasPermission(Manifest.permission.READ_CONTACTS); Boolean isIgnoring = Helper.isIgnoringOptimizations(getContext()); if (!setup_reminder || (hasPermissions && (isIgnoring == null || isIgnoring))) super.finish(); else { FragmentDialogStill fragment = new FragmentDialogStill(); fragment.setTargetFragment(this, ActivitySetup.REQUEST_STILL); fragment.show(getParentFragmentManager(), "setup:still"); } } public static class FragmentDialogStill extends FragmentDialogBase { @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_setup, null); CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); Group grp3 = dview.findViewById(R.id.grp3); Group grp4 = dview.findViewById(R.id.grp4); cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); prefs.edit().putBoolean("setup_reminder", !isChecked).apply(); } }); boolean hasPermissions = Helper.hasPermission(getContext(), Manifest.permission.READ_CONTACTS); Boolean isIgnoring = Helper.isIgnoringOptimizations(getContext()); grp3.setVisibility(hasPermissions ? View.GONE : View.VISIBLE); grp4.setVisibility(isIgnoring == null || isIgnoring ? View.GONE : View.VISIBLE); return new AlertDialog.Builder(getContext()) .setView(dview) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sendResult(Activity.RESULT_OK); } }) .setNegativeButton(android.R.string.cancel, null) .create(); } } private class PagerAdapter extends FragmentStatePagerAdapter { public PagerAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return 10; } @Override public Fragment getItem(int position) { switch (position) { case 0: return new FragmentSetup(); case 1: return new FragmentOptionsSynchronize(); case 2: return new FragmentOptionsSend(); case 3: return new FragmentOptionsConnection(); case 4: return new FragmentOptionsDisplay(); case 5: return new FragmentOptionsBehavior(); case 6: return new FragmentOptionsPrivacy(); case 7: return new FragmentOptionsEncryption(); case 8: return new FragmentOptionsNotifications(); case 9: return new FragmentOptionsMisc(); default: throw new IllegalArgumentException(); } } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return getString(R.string.title_advanced_section_main); case 1: return getString(R.string.title_advanced_section_synchronize); case 2: return getString(R.string.title_advanced_section_send); case 3: return getString(R.string.title_advanced_section_connection); case 4: return getString(R.string.title_advanced_section_display); case 5: return getString(R.string.title_advanced_section_behavior); case 6: return getString(R.string.title_advanced_section_privacy); case 7: return getString(R.string.title_advanced_section_encryption); case 8: return getString(R.string.title_advanced_section_notifications); case 9: return getString(R.string.title_advanced_section_misc); default: throw new IllegalArgumentException(); } } @Override public int getItemPosition(@NonNull Object object) { return POSITION_NONE; // always recreate fragment } } }