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-2019 by Marcel Bokhorst (M66B) */ import android.Manifest; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; import android.provider.Settings; 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.Button; import android.widget.ImageButton; import android.widget.TextView; import java.util.List; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; public class FragmentSetup extends FragmentEx { private ViewGroup view; private Button btnQuick; private TextView tvAccountDone; private Button btnAccount; private TextView tvNoPrimaryDrafts; private TextView tvIdentityDone; private Button btnIdentity; private TextView tvPermissionsDone; private Button btnPermissions; private TextView tvDozeDone; private Button btnDoze; private Button btnData; private Button btnInbox; private int textColorPrimary; private int colorWarning; private Drawable check; private static final String[] permissions = new String[]{ Manifest.permission.READ_CONTACTS }; @Override @Nullable public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { setSubtitle(R.string.title_setup); setHasOptionsMenu(true); textColorPrimary = Helper.resolveColor(getContext(), android.R.attr.textColorPrimary); colorWarning = Helper.resolveColor(getContext(), R.attr.colorWarning); check = getResources().getDrawable(R.drawable.baseline_check_24, getContext().getTheme()); view = (ViewGroup) inflater.inflate(R.layout.fragment_setup, container, false); // Get controls btnQuick = view.findViewById(R.id.btnQuick); tvAccountDone = view.findViewById(R.id.tvAccountDone); btnAccount = view.findViewById(R.id.btnAccount); tvNoPrimaryDrafts = view.findViewById(R.id.tvNoPrimaryDrafts); tvIdentityDone = view.findViewById(R.id.tvIdentityDone); btnIdentity = view.findViewById(R.id.btnIdentity); tvPermissionsDone = view.findViewById(R.id.tvPermissionsDone); btnPermissions = view.findViewById(R.id.btnPermissions); tvDozeDone = view.findViewById(R.id.tvDozeDone); btnDoze = view.findViewById(R.id.btnDoze); btnData = view.findViewById(R.id.btnData); btnInbox = view.findViewById(R.id.btnInbox); // Wire controls btnQuick.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.content_frame, new FragmentQuickSetup()).addToBackStack("quick"); fragmentTransaction.commit(); } }); btnAccount.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.content_frame, new FragmentAccounts()).addToBackStack("accounts"); fragmentTransaction.commit(); } }); btnIdentity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.content_frame, new FragmentIdentities()).addToBackStack("identities"); fragmentTransaction.commit(); } }); btnPermissions.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { btnPermissions.setEnabled(false); requestPermissions(permissions, ActivitySetup.REQUEST_PERMISSION); } }); btnDoze.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) .setMessage(R.string.title_setup_doze_instructions) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { try { startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)); } catch (Throwable ex) { Log.e(ex); } } }) .create() .show(); } }); btnData.setOnClickListener(new View.OnClickListener() { @Override @TargetApi(Build.VERSION_CODES.N) public void onClick(View v) { try { startActivity(new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID))); } catch (Throwable ex) { Log.e(ex); } } }); btnInbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); // Initialize tvAccountDone.setText(null); tvAccountDone.setCompoundDrawables(null, null, null, null); tvNoPrimaryDrafts.setVisibility(View.GONE); tvIdentityDone.setText(null); tvIdentityDone.setCompoundDrawables(null, null, null, null); btnIdentity.setEnabled(false); tvPermissionsDone.setText(null); tvPermissionsDone.setCompoundDrawables(null, null, null, null); tvDozeDone.setText(null); tvDozeDone.setCompoundDrawables(null, null, null, null); btnDoze.setEnabled(false); btnData.setVisibility(View.GONE); btnInbox.setEnabled(false); int[] grantResults = new int[permissions.length]; for (int i = 0; i < permissions.length; i++) grantResults[i] = ContextCompat.checkSelfPermission(getActivity(), permissions[i]); checkPermissions(permissions, grantResults, true); // Create outbox new SimpleTask() { @Override protected Void onExecute(Context context, Bundle args) { DB db = DB.getInstance(context); try { db.beginTransaction(); EntityFolder outbox = db.folder().getOutbox(); if (outbox == null) { outbox = new EntityFolder(); outbox.name = "OUTBOX"; outbox.type = EntityFolder.OUTBOX; outbox.level = 0; outbox.synchronize = false; outbox.sync_days = 0; outbox.keep_days = 0; outbox.id = db.folder().insertFolder(outbox); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } return null; } @Override protected void onException(Bundle args, Throwable ex) { Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); } }.execute(this, new Bundle(), "outbox:create"); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final DB db = DB.getInstance(getContext()); db.account().liveAccounts(true).observe(getViewLifecycleOwner(), new Observer>() { private boolean done = false; private LiveData livePrimaryDrafts = null; private LiveData livePrimaryArchive = null; @Override public void onChanged(@Nullable List accounts) { done = (accounts != null && accounts.size() > 0); getActivity().invalidateOptionsMenu(); tvAccountDone.setText(done ? R.string.title_setup_done : R.string.title_setup_to_do); tvAccountDone.setTextColor(done ? textColorPrimary : colorWarning); tvAccountDone.setCompoundDrawablesWithIntrinsicBounds(done ? check : null, null, null, null); btnIdentity.setEnabled(done); btnInbox.setEnabled(done); if (livePrimaryDrafts == null) livePrimaryDrafts = db.folder().livePrimaryDrafts(); else livePrimaryDrafts.removeObservers(getViewLifecycleOwner()); if (livePrimaryArchive == null) livePrimaryArchive = db.folder().livePrimaryArchive(); else livePrimaryArchive.removeObservers(getViewLifecycleOwner()); livePrimaryDrafts.observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(EntityFolder drafts) { tvNoPrimaryDrafts.setVisibility(done && drafts == null ? View.VISIBLE : View.GONE); } }); livePrimaryArchive.observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(EntityFolder archive) { PackageManager pm = getContext().getPackageManager(); pm.setComponentEnabledSetting( new ComponentName(getContext(), ActivitySearch.class), archive == null ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } }); } }); db.identity().liveIdentities(null, true).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List identities) { boolean done = (identities != null && identities.size() > 0); tvIdentityDone.setText(done ? R.string.title_setup_done : R.string.title_setup_to_do); tvIdentityDone.setTextColor(done ? textColorPrimary : colorWarning); tvIdentityDone.setCompoundDrawablesWithIntrinsicBounds(done ? check : null, null, null, null); } }); } @Override public void onResume() { super.onResume(); boolean ignoring = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); if (intent.resolveActivity(getContext().getPackageManager()) != null) { PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); ignoring = pm.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID); } } btnDoze.setEnabled(!ignoring); tvDozeDone.setText(ignoring ? R.string.title_setup_done : R.string.title_setup_to_do); tvDozeDone.setTextColor(ignoring ? textColorPrimary : colorWarning); tvDozeDone.setCompoundDrawablesWithIntrinsicBounds(ignoring ? check : null, null, null, null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ConnectivityManager cm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); boolean saving = (cm.getRestrictBackgroundStatus() == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED); btnData.setVisibility(saving ? View.VISIBLE : View.GONE); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_setup, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public void onPrepareOptionsMenu(Menu menu) { PackageManager pm = getContext().getPackageManager(); menu.findItem(R.id.menu_help).setVisible(getIntentHelp().resolveActivity(pm) != null); super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_help: onMenuHelp(); return true; default: return super.onOptionsItemSelected(item); } } private void onMenuHelp() { startActivity(getIntentHelp()); } private Intent getIntentHelp() { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("https://github.com/M66B/open-source-email/blob/master/SETUP.md#setup-help")); return intent; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == ActivitySetup.REQUEST_PERMISSION) checkPermissions(permissions, grantResults, false); } private void checkPermissions(String[] permissions, @NonNull int[] grantResults, boolean init) { boolean has = (grantResults.length > 0); for (int result : grantResults) if (result != PackageManager.PERMISSION_GRANTED) { has = false; break; } tvPermissionsDone.setText(has ? R.string.title_setup_done : R.string.title_setup_to_do); tvPermissionsDone.setTextColor(has ? textColorPrimary : colorWarning); tvPermissionsDone.setCompoundDrawablesWithIntrinsicBounds(has ? check : null, null, null, null); btnPermissions.setEnabled(!has); if (has && !init) new SimpleTask() { @Override protected Void onExecute(Context context, Bundle args) { DB db = DB.getInstance(context); for (EntityFolder folder : db.folder().getFoldersSynchronizing()) EntityOperation.sync(db, folder.id); return null; } @Override protected void onException(Bundle args, Throwable ex) { Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); } }.execute(FragmentSetup.this, new Bundle(), "setup:sync"); } }