Java tutorial
/* This file is part of My Expenses. * My Expenses 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. * * My Expenses 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 My Expenses. If not, see <http://www.gnu.org/licenses/>. */ package org.totschnig.myexpenses.dialog; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.OpenableColumns; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import org.totschnig.myexpenses.MyApplication; import org.totschnig.myexpenses.R; import org.totschnig.myexpenses.activity.ProtectedFragmentActivity; import org.totschnig.myexpenses.activity.ProtectionDelegate; import org.totschnig.myexpenses.export.qif.QifDateFormat; import org.totschnig.myexpenses.model.AccountType; import org.totschnig.myexpenses.model.ContribFeature; import org.totschnig.myexpenses.model.CurrencyEnum; import org.totschnig.myexpenses.preference.PrefKey; import org.totschnig.myexpenses.util.Utils; import java.util.Arrays; import java.util.List; /** * Util class with helper methods * @author Michael Totschnig * */ public class DialogUtils { private DialogUtils() { } /** * @return Dialog to be used from Preference, * and from version update */ public static Dialog sendWithFTPDialog(final Activity ctx) { return new AlertDialog.Builder(ctx).setMessage(R.string.no_app_handling_ftp_available) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ctx.dismissDialog(R.id.FTP_DIALOG); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(MyApplication.getMarketPrefix() + "org.totschnig.sendwithftp")); if (Utils.isIntentAvailable(ctx, intent)) { ctx.startActivity(intent); } else { Toast.makeText(ctx.getBaseContext(), R.string.error_accessing_market, Toast.LENGTH_LONG) .show(); } } }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ctx.dismissDialog(R.id.FTP_DIALOG); } }).create(); } public static void showPasswordDialog(final Activity ctx, AlertDialog dialog, boolean hideWindow, PasswordDialogUnlockedCallback callback) { if (hideWindow) { ctx.findViewById(android.R.id.content).setVisibility(View.GONE); if (ctx instanceof AppCompatActivity) { final ActionBar actionBar = ((AppCompatActivity) ctx).getSupportActionBar(); if (actionBar != null) actionBar.hide(); } } dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); dialog.show(); if (callback == null) { callback = new PasswordDialogUnlockedCallback() { @Override public void onPasswordDialogUnlocked() { MyApplication.getInstance().setLocked(false); ctx.findViewById(android.R.id.content).setVisibility(View.VISIBLE); if (ctx instanceof AppCompatActivity) { final ActionBar actionBar = ((AppCompatActivity) ctx).getSupportActionBar(); if (actionBar != null) actionBar.show(); } } }; } PasswordDialogListener passwordDialogListener = new PasswordDialogListener(ctx, dialog, callback); Button b = dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (b != null) b.setOnClickListener(passwordDialogListener); b = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); if (b != null) b.setOnClickListener(passwordDialogListener); } public static AlertDialog passwordDialog(final Activity ctx, final boolean cancelable) { final String securityQuestion = PrefKey.SECURITY_QUESTION.getString(""); LayoutInflater li = LayoutInflater.from(ctx); //noinspection InflateParams View view = li.inflate(R.layout.password_check, null); view.findViewById(R.id.password).setTag(Boolean.valueOf(false)); AlertDialog.Builder builder = new AlertDialog.Builder(ctx).setTitle(R.string.password_prompt).setView(view) .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (cancelable) { dialog.dismiss(); } else { ctx.moveTaskToBack(true); } } }); if (ContribFeature.SECURITY_QUESTION.hasAccess() && !securityQuestion.equals("")) { builder.setNeutralButton(R.string.password_lost, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); } builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); if (cancelable) { builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); } return builder.create(); } public static Uri handleFilenameRequestResult(Intent data, EditText mFilename, String typeName, UriTypePartChecker checker) { Uri mUri = data.getData(); String errorMsg; if (mUri != null) { Context context = MyApplication.getInstance(); mFilename.setError(null); String displayName = getDisplayName(mUri); mFilename.setText(displayName); if (displayName == null) { mUri = null; //SecurityException raised during getDisplayName errorMsg = "Error while retrieving document"; mFilename.setError(errorMsg); Toast.makeText(context, errorMsg, Toast.LENGTH_LONG).show(); } else { String type = context.getContentResolver().getType(mUri); if (type != null) { String[] typeParts = type.split("/"); if (typeParts.length == 0 || !checker.checkTypeParts(typeParts)) { mUri = null; errorMsg = context.getString(R.string.import_source_select_error, typeName); mFilename.setError(errorMsg); Toast.makeText(context, errorMsg, Toast.LENGTH_LONG).show(); } } } /* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mUri != null) { final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; try { //this probably will not succeed as long as we stick to ACTION_GET_CONTENT context.getContentResolver().takePersistableUriPermission(mUri, takeFlags); } catch (SecurityException e) { //Utils.reportToAcra(e); } }*/ } return mUri; } public interface UriTypePartChecker { boolean checkTypeParts(String[] typeParts); } public static boolean checkTypePartsDefault(String[] typeParts) { return typeParts[0].equals("*") || typeParts[0].equals("text") || typeParts[0].equals("application"); } //https://developer.android.com/guide/topics/providers/document-provider.html /** * @return display name for document stored at mUri. * Returns null if accessing mUri raises {@link SecurityException} */ @SuppressLint("NewApi") public static String getDisplayName(Uri uri) { if (!"file".equalsIgnoreCase(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // The query, since it only applies to a single document, will only return // one row. There's no need to filter, sort, or select fields, since we want // all fields for one document. try { Cursor cursor = MyApplication.getInstance().getContentResolver().query(uri, null, null, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { // Note it's called "Display Name". This is // provider-specific, and might not necessarily be the file name. int columnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (columnIndex != -1) { String displayName = cursor.getString(columnIndex); if (displayName != null) { return displayName; } } } } catch (Exception e) { } finally { cursor.close(); } } } catch (SecurityException e) { //this can happen if the user has restored a backup and //we do not have a persistable permision //return null; } } List<String> filePathSegments = uri.getPathSegments(); if (!filePathSegments.isEmpty()) { return filePathSegments.get(filePathSegments.size() - 1); } else { return "UNKNOWN"; } } public static interface PasswordDialogUnlockedCallback { void onPasswordDialogUnlocked(); } static class PasswordDialogListener implements View.OnClickListener { private final AlertDialog dialog; private final Activity ctx; private final PasswordDialogUnlockedCallback callback; public PasswordDialogListener(Activity ctx, AlertDialog dialog, PasswordDialogUnlockedCallback callback) { this.dialog = dialog; this.ctx = ctx; this.callback = callback; } @Override public void onClick(View v) { final SharedPreferences settings = MyApplication.getInstance().getSettings(); final String securityQuestion = PrefKey.SECURITY_QUESTION.getString(""); EditText input = (EditText) dialog.findViewById(R.id.password); TextView error = (TextView) dialog.findViewById(R.id.passwordInvalid); if (v == dialog.getButton(AlertDialog.BUTTON_NEUTRAL)) { if ((Boolean) input.getTag()) { input.setTag(Boolean.valueOf(false)); ((Button) v).setText(R.string.password_lost); dialog.setTitle(R.string.password_prompt); } else { input.setTag(Boolean.valueOf(true)); dialog.setTitle(securityQuestion); ((Button) v).setText(android.R.string.cancel); } } else { String value = input.getText().toString(); boolean isInSecurityQuestion = (Boolean) input.getTag(); if (Utils.md5(value).equals( (isInSecurityQuestion ? PrefKey.SECURITY_ANSWER : PrefKey.SET_PASSWORD).getString(""))) { input.setText(""); error.setText(""); callback.onPasswordDialogUnlocked(); if (isInSecurityQuestion) { PrefKey.PERFORM_PROTECTION.putBoolean(false); Toast.makeText(ctx.getBaseContext(), R.string.password_disabled_reenable, Toast.LENGTH_LONG) .show(); dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setText(R.string.password_lost); dialog.setTitle(R.string.password_prompt); input.setTag(Boolean.valueOf(false)); } dialog.dismiss(); } else { input.setText(""); error.setText(isInSecurityQuestion ? R.string.password_security_answer_not_valid : R.string.password_not_valid); } } } } public static RadioGroup configureCalendarRestoreStrategy(View view) { RadioGroup restorePlanStrategie = (RadioGroup) view.findViewById(R.id.restore_calendar_handling); String calendarId = PrefKey.PLANNER_CALENDAR_ID.getString("-1"); String calendarPath = PrefKey.PLANNER_CALENDAR_PATH.getString(""); RadioButton configured = (RadioButton) view.findViewById(R.id.restore_calendar_handling_configured); if ((calendarId.equals("-1")) || calendarPath.equals("")) { configured.setEnabled(false); } else { //noinspection SetTextI18n configured.setText(configured.getText() + " (" + calendarPath + ")"); } return restorePlanStrategie; } public static RadioGroup.OnCheckedChangeListener buildCalendarRestoreStrategyChangedListener( final ProtectedFragmentActivity context, final CalendarRestoreStrategyChangedListener listener) { return new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if (checkedId == R.id.restore_calendar_handling_backup || checkedId == R.id.restore_calendar_handling_configured) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_DENIED) { ActivityCompat.requestPermissions(context, new String[] { Manifest.permission.WRITE_CALENDAR }, ProtectionDelegate.PERMISSIONS_REQUEST_WRITE_CALENDAR); } } listener.onCheckedChanged(); } }; } public interface CalendarRestoreStrategyChangedListener { void onCheckedChanged(); void onCalendarPermissionDenied(); } public static Spinner configureDateFormat(View view, Context context, String prefName) { Spinner spinner = (Spinner) view.findViewById(R.id.DateFormat); ArrayAdapter<QifDateFormat> dateFormatAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, QifDateFormat.values()); dateFormatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(dateFormatAdapter); QifDateFormat qdf; try { qdf = QifDateFormat.valueOf(MyApplication.getInstance().getSettings().getString(prefName, "EU")); } catch (IllegalArgumentException e) { qdf = QifDateFormat.EU; } spinner.setSelection(qdf.ordinal()); return spinner; } public static Spinner configureEncoding(View view, Context context, String prefName) { Spinner spinner = (Spinner) view.findViewById(R.id.Encoding); spinner.setSelection( Arrays.asList(context.getResources().getStringArray(R.array.pref_qif_export_file_encoding)) .indexOf(MyApplication.getInstance().getSettings().getString(prefName, "UTF-8"))); return spinner; } public static Spinner configureDelimiter(View view, Context context, String prefName) { Spinner spinner = (Spinner) view.findViewById(R.id.Delimiter); spinner.setSelection( Arrays.asList(context.getResources().getStringArray(R.array.pref_csv_import_delimiter_values)) .indexOf(MyApplication.getInstance().getSettings().getString(prefName, ","))); return spinner; } public static EditText configureFilename(View view) { return (EditText) view.findViewById(R.id.Filename); } public static Spinner configureCurrencySpinner(View view, Context context, AdapterView.OnItemSelectedListener listener) { Spinner spinner = (Spinner) view.findViewById(R.id.Currency); ArrayAdapter<CurrencyEnum> curAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, android.R.id.text1, CurrencyEnum.sortedValues()); curAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(curAdapter); spinner.setOnItemSelectedListener(listener); spinner.setSelection( curAdapter.getPosition(CurrencyEnum.valueOf(Utils.getLocalCurrency().getCurrencyCode()))); return spinner; } public static Spinner configureTypeSpinner(View view, Context context) { Spinner spinner = (Spinner) view.findViewById(R.id.AccountType); ArrayAdapter<AccountType> typAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, android.R.id.text1, AccountType.values()); typAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(typAdapter); return spinner; } public static void openBrowse(Uri uri, Fragment fragment) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT);//TODO implement preference that allows to use ACTION_OPEN_DOCUMENT intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setDataAndType(uri, "*/*"); try { fragment.startActivityForResult(intent, ProtectedFragmentActivity.IMPORT_FILENAME_REQUESTCODE); } catch (ActivityNotFoundException e) { // No compatible file manager was found. Toast.makeText(fragment.getActivity(), R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show(); } catch (SecurityException ex) { Toast.makeText(fragment.getActivity(), String.format( "Sorry, this destination does not accept %s request. Please select a different one.", intent.getAction()), Toast.LENGTH_SHORT).show(); } } }