com.nuvolect.securesuite.main.SharedMenu.java Source code

Java tutorial

Introduction

Here is the source code for com.nuvolect.securesuite.main.SharedMenu.java

Source

/*
 * Copyright (c) 2017. Nuvolect LLC
 *
 * This program 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.
 *
 * Contact legal@nuvolect.com for a less restrictive commercial license if you would like to use the
 * software without the GPLv3 restrictions.
 *
 * This program 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 this program.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 */

package com.nuvolect.securesuite.main;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.v4.content.ContextCompat;
import android.view.MenuItem;
import android.widget.Toast;

import com.nuvolect.securesuite.R;
import com.nuvolect.securesuite.data.BackupRestore;
import com.nuvolect.securesuite.data.CleanupFragment;
import com.nuvolect.securesuite.data.ExportVcf;
import com.nuvolect.securesuite.data.ImportContacts;
import com.nuvolect.securesuite.data.ImportVcard;
import com.nuvolect.securesuite.data.MergeGroup;
import com.nuvolect.securesuite.data.MyContacts;
import com.nuvolect.securesuite.data.MyGroups;
import com.nuvolect.securesuite.data.SqlCipher;
import com.nuvolect.securesuite.data.SqlFullSyncSource;
import com.nuvolect.securesuite.license.AppSpecific;
import com.nuvolect.securesuite.util.Cryp;
import com.nuvolect.securesuite.util.DialogUtil;
import com.nuvolect.securesuite.util.FileBrowserImportVcf;
import com.nuvolect.securesuite.util.LogUtil;
import com.nuvolect.securesuite.util.PermissionUtil;
import com.nuvolect.securesuite.util.Persist;
import com.nuvolect.securesuite.util.Util;
import com.nuvolect.securesuite.util.WorkerCommand;

import net.sqlcipher.Cursor;

import static android.Manifest.permission.GET_ACCOUNTS;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static com.nuvolect.securesuite.data.BackupRestore.backupToStorage;
import static com.nuvolect.securesuite.data.MyContacts.invalidContact;

public class SharedMenu extends Activity {

    private static final int REQUEST_ID_BACKUP_TO_STORAGE = 123;

    public enum POST_CMD {
        ACT_RECREATE, REFRESH_LEFT_DEFAULT_RIGHT, DONE, NIL, START_CONTACT_EDIT, SETTINGS_FRAG, START_CONTACT_DETAIL
    }

    private static final boolean DEBUG = LogUtil.DEBUG;
    private static Activity m_act;
    private static MenuItem m_item;
    private static int m_item_id;
    private static long m_contact_id;
    private static int m_group_id;
    private static PostCmdCallbacks m_post_cmd_callbacks;
    private static Cursor m_cursor;

    public interface PostCmdCallbacks {

        void postCommand(SharedMenu.POST_CMD post_cmd);

    }

    public static void processCmd(Activity act, MenuItem item) {

        m_act = act;
        m_item = item;
        m_item_id = item.getItemId();

        process();

        return;
    }

    public static POST_CMD processCmd(Activity act, MenuItem item, long contact_id) {

        m_act = act;
        m_item = item;
        m_item_id = item.getItemId();
        m_cursor = null;
        m_contact_id = contact_id;
        m_cursor = null;

        return process();
    }

    /**
     * Full featured process command at the contact level.  Passing the cursor gives
     * better access to neighboring contacts in the case of a delete.
     * @param act
     * @param cursor
     * @param item_id
     * @return
     */
    public static POST_CMD processCmd(Activity act, Cursor cursor, int item_id) {

        m_act = act;
        m_item = null;
        m_cursor = cursor;
        m_item_id = item_id;
        m_contact_id = m_cursor.getLong(SqlCipher.index_contact_id);

        long contact_id = m_contact_id;
        if (DEBUG)
            LogUtil.log("SharedMenu processCmd: " + SqlCipher.contactInfo(contact_id));

        long next_id = SqlCipher.getNextContactId(m_cursor);
        if (DEBUG)
            LogUtil.log("SharedMenu processCmd next: " + SqlCipher.contactInfo(next_id));

        return process();
    }

    public static POST_CMD processCmd(Activity act, MenuItem item, long contact_id, int group_id,
            PostCmdCallbacks postCmdCallbacks) {
        m_act = act;
        m_item = item;
        m_item_id = item.getItemId();
        m_contact_id = contact_id;
        m_group_id = group_id;
        m_post_cmd_callbacks = postCmdCallbacks;
        m_cursor = null;

        return process();
    }

    public static POST_CMD processCmd(Activity act, MenuItem item, long contact_id,
            PostCmdCallbacks postCmdCallbacks) {
        m_act = act;
        m_item = item;
        m_item_id = item.getItemId();
        m_contact_id = contact_id;
        m_post_cmd_callbacks = postCmdCallbacks;
        m_cursor = null;

        return process();
    }

    public static POST_CMD processCmd(Activity act, MenuItem item, int group_id,
            PostCmdCallbacks postCmdCallbacks) {
        m_act = act;
        m_item = item;
        m_item_id = item.getItemId();
        m_contact_id = 0;
        m_group_id = group_id;
        m_post_cmd_callbacks = postCmdCallbacks;
        m_cursor = null;

        return process();
    }

    /**
     * Process a menu command and return a post command to be executed in the
     * calling activity
     * @return post command.
     */
    private static POST_CMD process() {

        switch (m_item_id) {

        case R.id.menu_shared_search: {
            SearchDialog.manageSearch(m_act, new SearchDialog.SearchCallbacks() {
                @Override
                public void onContactSelected(long contact_id) {

                    if (invalidContact(contact_id)) {
                        Toast.makeText(m_act, "Invalid contact", Toast.LENGTH_SHORT).show();
                        return;
                    }

                    LogUtil.log("SharedMenu.shared_search id selected: " + contact_id);
                    Persist.setCurrentContactId(m_act, contact_id);
                    if (m_post_cmd_callbacks == null) {

                        LogUtil.log("ERROR SharedMenu.shared_search callbacks null ");
                    } else {

                        m_post_cmd_callbacks.postCommand(POST_CMD.START_CONTACT_DETAIL);
                    }
                }
            });
            break;
        }
        case R.id.menu_add_contact: {
            m_contact_id = SqlCipher.createEmptyContact(m_act, Cryp.getCurrentGroup());
            Persist.setCurrentContactId(m_act, m_contact_id);

            // In case the user discards contact, it will be deleted
            Persist.setEmptyContactId(m_act, m_contact_id);
            return POST_CMD.START_CONTACT_EDIT;
        }
        case R.id.menu_delete_contact: {
            if (invalidContact(m_contact_id)) {
                Toast.makeText(m_act, "Select a contact to delete", Toast.LENGTH_SHORT).show();
                return POST_CMD.DONE;
            } else {
                long nextId = 0;
                if (m_cursor != null)
                    nextId = SqlCipher.getNextContactId(m_cursor);

                boolean success;
                if (MyGroups.isInTrash(m_contact_id)) {

                    success = 1 == SqlCipher.deleteContact(m_act, m_contact_id, true);//sync==true
                    if (success)
                        Toast.makeText(m_act, "Contact deleted", Toast.LENGTH_SHORT).show();
                } else {

                    success = MyGroups.trashContact(m_act, Cryp.getCurrentAccount(), m_contact_id);
                    if (success)
                        Toast.makeText(m_act, "Item moved to trash", Toast.LENGTH_SHORT).show();
                }
                if (!success)
                    LogUtil.log("SharedMenu.delete_contact delete failure: " + m_contact_id);

                MyContacts.setValidId(m_act, nextId);

                MyGroups.loadGroupMemory();
                return POST_CMD.REFRESH_LEFT_DEFAULT_RIGHT;
            }
        }
        case R.id.menu_edit_contact: {
            if (invalidContact(m_contact_id)) {
                Toast.makeText(m_act, "Invalid contact", Toast.LENGTH_SHORT).show();
                return POST_CMD.NIL;
            } else
                return POST_CMD.START_CONTACT_EDIT;
        }
        case R.id.menu_edit_save:
        case R.id.menu_edit_discard: {
            return POST_CMD.START_CONTACT_DETAIL;
        }
        case R.id.menu_share_contact: {
            if (invalidContact(m_contact_id)) {
                Toast.makeText(m_act, "Invalid contact", Toast.LENGTH_SHORT).show();
                return POST_CMD.NIL;
            }
            ExportVcf.emailVcf(m_act, Persist.getCurrentContactId(m_act));
            return POST_CMD.NIL;
        }
        case R.id.menu_set_profile:
            Persist.setProfileId(m_act, m_contact_id);
            Toast.makeText(m_act, "Profile set", Toast.LENGTH_SHORT).show();
            return POST_CMD.REFRESH_LEFT_DEFAULT_RIGHT;

        case R.id.menu_delete_group: {

            // Test for All other contacts group, it can't be deleted
            if (MyGroups.isBaseGroup(m_group_id)) {

                Toast.makeText(m_act, "This group cannot be removed", Toast.LENGTH_LONG).show();
            } else {
                // Delete the group but not the contacts in the group.
                // Remove memory based and DB group title and data records matching group ID.
                // Sync transaction to companion device.
                MyGroups.deleteGroup(m_act, m_group_id, true);// Sync transaction is true

                // Group is now gone, select a default group
                int g_id = MyGroups.getDefaultGroup(Cryp.getCurrentAccount());
                Cryp.setCurrentGroup(g_id);

                return POST_CMD.REFRESH_LEFT_DEFAULT_RIGHT;
            }
            break;
        }
        case R.id.menu_add_group: {
            Intent intent = new Intent(m_act, EditGroupActivity.class);
            m_act.startActivity(intent);
            break;
        }
        case R.id.menu_edit_group: {
            if (MyGroups.isPseudoGroup(m_group_id))
                Toast.makeText(m_act, "Can't edit this group directly", Toast.LENGTH_SHORT).show();
            else {
                Intent intent = new Intent(m_act, EditGroupActivity.class);
                intent.putExtra(CConst.GROUP_ID_KEY, Cryp.getCurrentGroup());
                m_act.startActivity(intent);
            }
            break;
        }
        case R.id.menu_merge_group:
            MergeGroup.mergeGroup(m_act);
            break;
        //FUTURE integrate OpenPGP https://github.com/open-keychain/openpgp-api
        case R.id.menu_email_group:
            GroupSendEmail.emailGroup(m_act);
            break;
        case R.id.menu_text_group: {
            GroupSendSms.startGroupSms(m_act);
            break;
        }
        case R.id.menu_import_vcard: {

            if (hasPermission(READ_EXTERNAL_STORAGE)) {

                // Kickoff a browser activity here.
                // When user selects file, onActivityResult called with the result.
                Intent intent = new Intent();
                intent.setClass(m_act, FileBrowserImportVcf.class);
                m_act.startActivityForResult(intent, CConst.IMPORT_VCARD_BROWSE_ACTION);
            } else
                PermissionUtil.requestReadExternalStorage(m_act, CConst.IMPORT_VCARD_REQUEST_EXTERNAL_STORAGE);
            break;
        }
        case R.id.menu_import_single_contact: {

            if (hasPermission(READ_CONTACTS)) {
                /**
                 * Launch the contact picker intent.
                 * Results returned in onActivityResult()
                 */
                Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
                m_act.startActivityForResult(contactPickerIntent, CConst.IMPORT_SINGLE_CONTACT_PICKER);
            } else
                PermissionUtil.requestReadExternalStorage(m_act, CConst.IMPORT_SINGLE_CONTACT_REQUEST_READ);
            break;
        }
        case R.id.menu_import_account_contacts: {

            if (hasPermission(READ_CONTACTS) && hasPermission(GET_ACCOUNTS)) {

                CloudImportDialog.openDialog(m_act);

            } else {
                if (!hasPermission(READ_CONTACTS)) {

                    PermissionUtil.requestReadContacts(m_act, CConst.IMPORT_ACCOUNT_CONTACTS_REQUEST_READ_CONTACTS);
                    break;
                }
                if (!hasPermission(GET_ACCOUNTS)) {

                    PermissionUtil.requestGetAccounts(m_act, CConst.IMPORT_ACCOUNT_CONTACTS_REQUEST_GET_ACCOUNTS);
                    break;
                }
            }
            break;
        }
        case R.id.menu_export_group: {

            if (hasPermission(WRITE_EXTERNAL_STORAGE)) {

                ExportVcf.exportGroupVcardAsync(m_act, m_group_id);
                Toast.makeText(m_act, "Exporting...", Toast.LENGTH_LONG).show();
            } else {
                PermissionUtil.requestWriteExternalStorage(m_act, 0);
            }
            break;
        }
        case R.id.menu_export_account: {

            if (hasPermission(WRITE_EXTERNAL_STORAGE)) {
                ExportVcf.exportAccountVcardAsync(m_act, Cryp.getCurrentAccount());
                Toast.makeText(m_act, "Exporting...", Toast.LENGTH_LONG).show();
            } else {
                PermissionUtil.requestWriteExternalStorage(m_act, 0);
            }
            break;
        }
        case R.id.menu_export_all: {

            if (hasPermission(WRITE_EXTERNAL_STORAGE)) {
                ExportVcf.exportAllVcardAsync(m_act);
                Toast.makeText(m_act, "Exporting...", Toast.LENGTH_LONG).show();
            } else {
                PermissionUtil.requestWriteExternalStorage(m_act, 0);
            }
            break;
        }
        case R.id.menu_cleanup: {
            CleanupFragment f = CleanupFragment.newInstance(m_act);
            f.startFragment();
            break;
        }
        case R.id.menu_backup_to_email: {
            if (hasPermission(WRITE_EXTERNAL_STORAGE)) {
                BackupRestore.backupToEmail(m_act);
            } else {
                PermissionUtil.requestWriteExternalStorage(m_act, 0);
            }
            break;
        }
        case R.id.menu_backup_to_storage: {

            if (hasPermission(WRITE_EXTERNAL_STORAGE))
                BackupRestore.backupToStorage(m_act);
            else
                PermissionUtil.requestWriteExternalStorage(m_act, REQUEST_ID_BACKUP_TO_STORAGE);
            break;
        }
        case R.id.menu_backup_to_lan: {

            SqlFullSyncSource.getInstance().backupDiag(m_act);
            break;
        }
        case R.id.menu_accounts: {
            // import android.provider.Settings;
            m_act.startActivity(new Intent(Settings.ACTION_SYNC_SETTINGS));
            break;
        }
        case R.id.menu_password_generator: {
            PasswordFragment f = PasswordFragment.newInstance(m_act);
            f.start();
            break;
        }
        case R.id.menu_empty_trash: {

            DialogUtil.emptyTrash(m_act, m_post_cmd_callbacks);
            break;
        }
        case R.id.menu_settings: {
            Intent intent = new Intent(m_act, SettingsActivity.class);
            m_act.startActivity(intent);
            break;
        }
        case R.id.menu_help: {
            String url = AppSpecific.APP_WIKI_URL;
            Intent i = new Intent(Intent.ACTION_VIEW);
            i.setData(Uri.parse(url));
            m_act.startActivity(i);
            break;
        }
        case R.id.menu_developer: {

            DeveloperDialog.start(m_act);
            break;
        }
        default:
            if (DEBUG && m_item != null)
                LogUtil.log("SharedMenu.default: " + m_item.getTitle());
        }
        return POST_CMD.NIL;
    }

    private static boolean hasPermission(String perm) {
        return (ContextCompat.checkSelfPermission(m_act, perm) == PackageManager.PERMISSION_GRANTED);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode) {

        case REQUEST_ID_BACKUP_TO_STORAGE: {

            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                backupToStorage(m_act);
            }
            break;
        }
        default:
        }
    }

    public static void sharedOnRequestPermissionsResult(Activity act, int requestCode, String[] permissions,
            int[] grantResults) {

        switch (requestCode) {

        /**
         * Service the result of a permission request
         */
        case CConst.IMPORT_VCARD_REQUEST_EXTERNAL_STORAGE: {

            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // Kickoff a browser activity here.
                // When user selects file, onActivityResult called with the result.
                Intent intent = new Intent();
                intent.setClass(act, FileBrowserImportVcf.class);
                act.startActivityForResult(intent, CConst.IMPORT_VCARD_BROWSE_ACTION);
            } else {

                Toast.makeText(act, "Sorry, external storage permission required for import", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        }
        case CConst.IMPORT_SINGLE_CONTACT_REQUEST_READ: {
            /**
             * Launch the contact picker intent.
             * Results returned in onActivityResult()
             */
            Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
            m_act.startActivityForResult(contactPickerIntent, CConst.IMPORT_SINGLE_CONTACT_PICKER);
            break;
        }
        case CConst.IMPORT_ACCOUNT_CONTACTS_REQUEST_READ_CONTACTS: {

            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                CloudImportDialog.openDialog(act);

            } else {

                Toast.makeText(m_act, "Sorry, read contacts permission required for import", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        }
        case CConst.CALLER_ID_REQUEST_READ_PHONE_STATE:
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // Do nothing
                //                    Toast.makeText(m_act, "Caller identification enabled", Toast.LENGTH_SHORT).show();

            } else {

                Toast.makeText(m_act, "Calls will not be identified without Read Phone State", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        case CConst.REQUEST_WRITE_EXTERNAL_STORAGE:
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // Do nothing
            } else {
                Toast.makeText(m_act, "App will be unable to save files to external storage", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        default: {

            LogUtil.log("SharedMenu sharedOnRequestPermissionResult UNMANAGED/ERROR requestCode: " + requestCode);

            for (int i = 0; i < permissions.length; i++)
                LogUtil.log("SharedMenuUtil permissions: " + permissions[i] + "  " + grantResults[i]);

        }
        }
    }

    public static void sharedOnActivityResult(Activity act, int requestCode, int resultCode, Intent data) {

        if (DEBUG)
            LogUtil.log("SharedMenu myOnActivityResult requestCode: " + requestCode);

        switch (requestCode) {

        case CConst.IMPORT_VCARD_BROWSE_ACTION: {

            if (resultCode == RESULT_OK) {

                Bundle activityResultBundle = data.getExtras();
                String path = activityResultBundle.getString(CConst.IMPORT_VCF_PATH);

                new ImportVcardAsync().execute(path);
            }
            break;
        }
        case CConst.IMPORT_SINGLE_CONTACT_PICKER: {

            if (resultCode == RESULT_OK) {

                Uri result = data.getData();
                String id = result.getLastPathSegment();
                LogUtil.log("Cloud contact ID: " + id);
                boolean success = true;

                if (id == null || id.isEmpty())
                    success = false;
                else {

                    long cloud_contact_id = Long.valueOf(id);
                    success = ImportContacts.importSingleContact(m_act, cloud_contact_id);
                }
                if (!success)
                    Toast.makeText(m_act, "Contact import error", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(m_act, "Contact imported", Toast.LENGTH_SHORT).show();
            }
            break;
        }
        case CConst.RESPONSE_CODE_SHARE_VCF: {

            LogUtil.log("SharedMenu sharedOnActivityResult SHARE_VCF delete ");
            Util.cleanupTempFolder(act);
            break;
        }
        default: {

            LogUtil.log("sharedOnActivityResult UNMANAGED/ERROR requestCode: " + requestCode);
        }
        }
    }

    private static class ImportVcardAsync extends AsyncTask<String, Integer, Long> {

        /** progress dialog to show user that the import is processing. */
        private ProgressDialog m_importProgressDialog = null;

        public ImportVcardAsync() {

            m_importProgressDialog = new ProgressDialog(m_act);
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            m_importProgressDialog.setMessage("Import starting...");
            m_importProgressDialog.show();
        }

        @Override
        protected Long doInBackground(String... paths) {

            String path = paths[0];

            ImportVcard.ImportProgressCallbacks callbacks = new ImportVcard.ImportProgressCallbacks() {
                @Override
                public void progressReport(int importProgress) {

                    publishProgress(importProgress);
                }
            };

            long contact_id = ImportVcard.importVcf(m_act, path, Cryp.getCurrentGroup(), callbacks);

            return contact_id;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            int vcardsImported = values[0];

            if (m_importProgressDialog == null) {
                m_importProgressDialog = new ProgressDialog(m_act);
                m_importProgressDialog.show();
            }

            if (m_importProgressDialog != null && m_importProgressDialog.isShowing())
                m_importProgressDialog.setMessage("Import progress: " + vcardsImported);
        }

        @Override
        protected void onPostExecute(Long contact_id) {

            if (m_importProgressDialog != null && m_importProgressDialog.isShowing())
                m_importProgressDialog.dismiss();

            if (contact_id > 0)
                Toast.makeText(m_act, "Import complete", Toast.LENGTH_LONG).show();
            else
                Toast.makeText(m_act, "Import failed", Toast.LENGTH_LONG).show();

            m_act.setProgressBarIndeterminateVisibility(false);

            WorkerCommand.refreshUserInterface(m_act, CConst.RECREATE);
            //FUTURE send message to refresh UI or specific fragments
            //            m_act.startContactListFragment();
            //            if( mTwoPane)
            //                startContactDetailFragment();
        }
    }
}