com.tinfoil.sms.settings.EditNumber.java Source code

Java tutorial

Introduction

Here is the source code for com.tinfoil.sms.settings.EditNumber.java

Source

/** 
 * Copyright (C) 2013 Jonathan Gillett, Joseph Heron
 * 
 * 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.
 *
 * 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.tinfoil.sms.settings;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.bugsense.trace.BugSenseHandler;
import com.tinfoil.sms.R;
import com.tinfoil.sms.crypto.KeyExchange;
import com.tinfoil.sms.dataStructures.Number;
import com.tinfoil.sms.dataStructures.TrustedContact;
import com.tinfoil.sms.database.DBAccessor;
import com.tinfoil.sms.utility.SMSUtility;

/**
 * TODO add remove initiator flag.
 * An activity used to edit a single number from a given contact. 
 * 
 * This activity returns a AddContact.RESULT_CODE if changes have been made and
 * the list needs to be update, else returns default result code.
 * 
 * The Extras are included are:
 * EditNumber.UPDATE, whether the number has been updated or not
 * EditNumber.NUMBER, the number that has been edited
 * AddContact.POSITION, the position of the given number in the contact's
 * numbers array list, -1 if it is a new number
 * EditNumber.ADD, whether the number is to update or deleted
 */
public class EditNumber extends Activity {

    public static final String UPDATE = "update";
    public static final String NUMBER = "number";
    public static final String ADD = "add";
    public static final String DELETE = "delete";
    public static final String IS_DELETED = "is_deleted";

    private EditText phoneNumber;
    private EditText sharedInfo1;
    private EditText sharedInfo2;
    private EditText bookPath;
    private EditText bookInverse;
    private EditText pubKey;
    private Number number;
    private String originalNumber;
    private static int position;
    private String name;

    public static final int SHARED_INFO_MIN = 6;
    public static final int SHARED_INFO_MAX = 128;

    private ArrayList<RadioButton> keyExchangeSetting;
    private DBAccessor dba;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.edit_number);
        setupActionBar();

        dba = new DBAccessor(this);

        keyExchangeSetting = new ArrayList<RadioButton>();

        keyExchangeSetting.add((RadioButton) this.findViewById(R.id.auto_exchange));
        keyExchangeSetting.add((RadioButton) this.findViewById(R.id.manual_exchange));
        keyExchangeSetting.add((RadioButton) this.findViewById(R.id.ignore_exchange));

        phoneNumber = (EditText) findViewById(R.id.phone_number);
        sharedInfo1 = (EditText) findViewById(R.id.shared_secret_1);
        sharedInfo2 = (EditText) findViewById(R.id.shared_secret_2);
        bookPath = (EditText) findViewById(R.id.book_path);
        bookInverse = (EditText) findViewById(R.id.book_inverse);

        pubKey = (EditText) findViewById(R.id.contact_pub_key);
        boolean trusted = false;

        Intent intent = this.getIntent();

        /* Check if there is intent */
        if (intent != null) {
            /*
             * Get the extra values in the intent.
             */
            name = intent.getStringExtra(AddContact.NAME);
            originalNumber = intent.getStringExtra(AddContact.EDIT_NUMBER);
            position = intent.getIntExtra(AddContact.POSITION, AddContact.NEW_NUMBER_CODE);
            this.getIntent().removeExtra(AddContact.NAME);
            this.getIntent().removeExtra(AddContact.EDIT_NUMBER);
            this.getIntent().removeExtra(AddContact.POSITION);
        } else {
            finish();
        }

        /*
         * Is the number a new number
         */
        if (position != AddContact.NEW_NUMBER_CODE) {
            /*
             * Initialize the values to be adjusted
             */
            number = dba.getNumber(SMSUtility.format(originalNumber));

            // Number was not found in the database
            if (number == null) {
                finish();
            }

            phoneNumber.setText(originalNumber);

            sharedInfo1.setText(number.getSharedInfo1());

            sharedInfo2.setText(number.getSharedInfo2());

            bookPath.setText(number.getBookPath());

            bookInverse.setText(number.getBookInversePath());

            keyExchangeSetting.get(number.getKeyExchangeFlag()).setChecked(true);

            if (dba.isTrustedContact(originalNumber)) {
                trusted = true;
            }
        } else {
            /*
             * Initialize the values to the default values
             */
            bookPath.setText(DBAccessor.DEFAULT_BOOK_PATH);

            bookInverse.setText(DBAccessor.DEFAULT_BOOK_INVERSE_PATH);
        }

        if (trusted) {
            TextView pubKeyView = (TextView) findViewById(R.id.view_pub_key);

            sharedInfo1.setEnabled(false);
            sharedInfo2.setEnabled(false);
            phoneNumber.setEnabled(false);
            //pubKeyTitle.setVisibility(TextView.VISIBLE);
            pubKey.setVisibility(EditText.INVISIBLE);

            pubKeyView.setVisibility(TextView.VISIBLE);
            pubKeyView.setText(new String(number.getPublicKey()));

            // Find the radio button group view
            View rg = (View) findViewById(R.id.radioGroup1);

            /*
             * Set it so now it is below book_inverse since the other two
             * elements are invisible.
             */
            RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            p.addRule(RelativeLayout.BELOW, R.id.view_pub_key);
            rg.setLayoutParams(p);
        }
    }

    /**
      * The onClick action for when the user clicks on save information
      * @param view The view that is involved
      */
    public void saveNumberInfo(View view) {
        String s1 = sharedInfo1.getText().toString();
        String s2 = sharedInfo2.getText().toString();
        if (((s1 == null || s1.length() == 0) && (s2 == null || s2.length() == 0))
                || (SMSUtility.checksharedSecret(s1) && SMSUtility.checksharedSecret(s2))) {
            /* Is there a valid number */
            if (phoneNumber.getText().toString() != null && phoneNumber.getText().toString().length() > 0) {
                //Number tempNumber;
                TrustedContact tc = null;

                if (number == null) {
                    /* Number is a new number */
                    number = new Number(phoneNumber.getText().toString());
                } else {
                    /*
                     * Editing the current number. Get the Number row
                     * for the previous number.
                     */

                    number.setNumber(phoneNumber.getText().toString());
                }

                /*
                 * Set the updated information
                 */
                //String newSharedInfo1 = s1;
                //String newSharedInfo2 = s2;

                if (SMSUtility.isChanged(number.getSharedInfo1(), s1)
                        || SMSUtility.isChanged(number.getSharedInfo2(), s2)) {
                    number.setInitiator(false);
                }

                number.setSharedInfo1(s1);
                number.setSharedInfo2(s2);
                number.setBookPath(bookPath.getText().toString());
                number.setBookInversePath(bookInverse.getText().toString());

                if (pubKey.getVisibility() == EditText.VISIBLE) {
                    String key = pubKey.getText().toString().trim();
                    if (key != "" && key.length() > 0) {
                        number.setPublicKey(key.getBytes());
                    }
                }

                int index = 0;
                if (keyExchangeSetting.get(Number.AUTO).isChecked()) {
                    index = Number.AUTO;
                } else if (keyExchangeSetting.get(Number.MANUAL).isChecked()) {
                    index = Number.MANUAL;
                } else {
                    index = Number.IGNORE;
                }
                number.setKeyExchangeFlag(index);

                /*
                 * Update/Add the number to the database
                 */
                if (originalNumber != null) {
                    if (position == AddContact.NEW_NUMBER_CODE) {
                        if (checkUniqueNumber()) {
                            tc = dba.getRow(originalNumber);
                            tc.addNumber(number);
                            dba.updateRow(tc, originalNumber);
                        } else {
                            finish();
                        }
                    } else {
                        if (number.getNumber().equals(originalNumber) || checkUniqueNumber()) {
                            dba.updateNumberRow(number, originalNumber, 0);
                        } else {
                            finish();
                        }
                    }
                } else {
                    if (checkUniqueNumber()) {
                        tc = new TrustedContact();
                        tc.addNumber(number);
                        dba.addRow(tc);
                    } else {
                        finish();
                    }
                }

                Intent data = new Intent();

                /*
                 * Return intent with given parameters 
                 */
                if (originalNumber != null && number.getNumber().equalsIgnoreCase(originalNumber)) {
                    data.putExtra(EditNumber.UPDATE, false);
                } else {
                    data.putExtra(EditNumber.UPDATE, true);
                }

                data.putExtra(EditNumber.NUMBER, number.getNumber());
                data.putExtra(AddContact.POSITION, position);
                data.putExtra(EditNumber.ADD, true);
                data.putExtra(AddContact.NAME, name);

                /*
                 * Set result code to identify that whether the list in
                 * ManageContactsActivity.
                 */
                EditNumber.this.setResult(AddContact.REQUEST_CODE, data);

                EditNumber.this.finish();
            }
        } else {
            //TODO create a better notification for invalid shared secrets (since they key board blocks them)
            Toast.makeText(this, this.getString(R.string.shared_secret) + SHARED_INFO_MIN, Toast.LENGTH_LONG)
                    .show();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        final MenuInflater inflater = this.getMenuInflater();
        inflater.inflate(R.menu.edit_text_menu, menu);
        return true;
    }

    private String[] fileNames;
    private AlertDialog popup_alert;

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {

        switch (item.getItemId()) {
        case android.R.id.home:
            // This ID represents the Home or Up button. In the case of this
            // activity, the Up button is shown. Use NavUtils to allow users
            // to navigate up one level in the application structure. For
            // more details, see the Navigation pattern on Android Design:
            //
            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
            //
            NavUtils.navigateUpFromSameTask(this);
            return true;
        case R.id.import_key:

            importKey();
            return true;

        case R.id.delete:

            deleteNumber();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    public static void exportOrSend(Context context, final Number number) {
        final DBAccessor dba = new DBAccessor(context);
        final String name = dba.getRow(number.getNumber()).getName();
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(
                context.getString(R.string.key_exchange_respond) + " " + name + ", " + number.getNumber() + "?")
                .setCancelable(true).setPositiveButton(R.string.sms_option, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        dba.addMessageToQueue(number.getNumber(), KeyExchange.sign(number, dba, SMSUtility.user),
                                true);
                    }
                }).setNegativeButton(R.string.export_option, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        UserKeySettings.writeToFile(number.getNumber(),
                                KeyExchange.sign(number, dba, SMSUtility.user));
                    }
                });

        AlertDialog alert = builder.create();
        alert.show();
    }

    private boolean checkUniqueNumber() {
        if (dba.inDatabase(number.getNumber())) {
            Intent returnIntent = new Intent();
            setResult(RESULT_CANCELED, returnIntent);

            Toast.makeText(this, R.string.number_exists, Toast.LENGTH_SHORT).show();

            return false;
        }
        return true;
    }

    /**
    * Set up the {@link android.app.ActionBar}, if the API is available.
    */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void setupActionBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            getActionBar().setDisplayHomeAsUpEnabled(true);
        }
    }

    private void deleteNumber() {
        Intent data = new Intent();

        if (originalNumber == null || originalNumber == "" || dba.getRow(originalNumber) == null) {
            // User deleted the number so 
            finish();
        } else {
            if (dba.getRow(originalNumber).getNumbers().size() == 1) {
                dba.removeRow(originalNumber);
                data.putExtra(EditNumber.IS_DELETED, true);
            } else {
                dba.deleteNumber(originalNumber);
            }
        }

        data.putExtra(EditNumber.UPDATE, true);
        data.putExtra(AddContact.POSITION, position);
        data.putExtra(EditNumber.ADD, true);
        data.putExtra(EditNumber.DELETE, originalNumber);
        setResult(AddContact.REQUEST_CODE, data);

        finish();
    }

    private void importKey() {
        if (originalNumber == null || originalNumber == "" || dba.getRow(originalNumber) == null) {
            Toast.makeText(getApplicationContext(), R.string.number_must_exist, Toast.LENGTH_LONG).show();
        } else {
            if (SMSUtility.isMediaAvailable() && number != null
                    && SMSUtility.checksharedSecret(number.getSharedInfo1())
                    && SMSUtility.checksharedSecret(number.getSharedInfo2())) {
                final File root = Environment.getExternalStorageDirectory();

                File dir = new File(root.getAbsolutePath() + UserKeySettings.path + "/");
                File[] contents = dir.listFiles();

                if (contents.length > 0) {
                    fileNames = new String[contents.length];
                    for (int i = 0; i < contents.length; i++) {
                        fileNames[i] = contents[i].getAbsolutePath();
                    }

                    final AlertDialog.Builder popup_builder = new AlertDialog.Builder(this);

                    popup_builder.setTitle(R.string.export_number_title).setCancelable(true)
                            .setSingleChoiceItems(fileNames, 0, new DialogInterface.OnClickListener() {

                                public void onClick(final DialogInterface dialog, final int which) {

                                    popup_alert.dismiss();
                                    String selectedFile = (String) fileNames[which];

                                    if (selectedFile != null) {
                                        StringBuilder sb = new StringBuilder();

                                        File pubKey = new File(selectedFile);

                                        try {
                                            FileInputStream f = new FileInputStream(pubKey);

                                            BufferedReader bufferedReader = new BufferedReader(
                                                    new InputStreamReader(f));
                                            String line;
                                            while ((line = bufferedReader.readLine()) != null) {
                                                sb.append(line);
                                            }
                                            bufferedReader.close();
                                            f.close();
                                        } catch (FileNotFoundException e) {
                                            e.printStackTrace();
                                            BugSenseHandler.sendExceptionMessage("Type",
                                                    "Import Public Key Not Found Error", e);
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                            BugSenseHandler.sendExceptionMessage("Type", "Import Public Key Error",
                                                    e);
                                        }

                                        String keyExchangeMessage = sb.toString();

                                        if (KeyExchange.isKeyExchange(keyExchangeMessage)) {
                                            if (KeyExchange.verify(number, keyExchangeMessage)) {
                                                Log.v("Key Exchange", "Exchange Key Message Received");

                                                number.setPublicKey(KeyExchange.encodedPubKey(keyExchangeMessage));
                                                number.setSignature(
                                                        KeyExchange.encodedSignature(keyExchangeMessage));

                                                dba.updateNumberRow(number, number.getNumber(), 0);
                                                Toast.makeText(EditNumber.this, R.string.key_exchange_import,
                                                        Toast.LENGTH_SHORT).show();

                                                if (!number.isInitiator()) {
                                                    Log.v("Key Exchange", "Not Initiator");
                                                    //MessageService.dba.addMessageToQueue(number.getNumber(),
                                                    //   KeyExchange.sign(number), true);
                                                    exportOrSend(EditNumber.this, number);
                                                }

                                                EditNumber.this.setResult(AddContact.UPDATED_NUMBER);
                                            }
                                        }

                                    }
                                }
                            });

                    popup_alert = popup_builder.create();
                    popup_alert.show();
                } else {
                    Toast.makeText(this, R.string.empty_key_exchange_list, Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

}