package com.iiordanov.bVNC; import java.util.ArrayList; import java.util.Collections; import net.sqlcipher.database.SQLiteDatabase; import com.iiordanov.bVNC.dialogs.IntroTextDialog; import com.iiordanov.bVNC.dialogs.GetTextFragment; import com.iiordanov.pubkeygenerator.GeneratePubkeyActivity; import android.app.Activity; import android.app.ActivityManager.MemoryInfo; import android.support.v4.app.FragmentTransaction; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Point; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.Log; import android.view.Display; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewConfiguration; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import android.content.res.Configuration; import com.iiordanov.bVNC.*; import com.iiordanov.freebVNC.*; import com.iiordanov.aRDP.*; import com.iiordanov.freeaRDP.*; import com.iiordanov.aSPICE.*; import com.iiordanov.freeaSPICE.*; public abstract class MainConfiguration extends FragmentActivity implements GetTextFragment.OnFragmentDismissedListener { private final static String TAG = "MainConfiguration"; private boolean togglingMasterPassword = false; protected ConnectionBean selected; protected Database database; protected Spinner spinnerConnection; protected EditText textNickname; protected boolean startingOrHasPaused = true; protected int layoutID; GetTextFragment getPassword = null; GetTextFragment getNewPassword = null; private boolean isConnecting = false; private Button buttonGeneratePubkey; protected abstract void updateViewFromSelected(); protected abstract void updateSelectedFromView(); @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Utils.showMenu(this); setContentView(layoutID); System.gc(); if (getPassword == null) { getPassword = GetTextFragment.newInstance(getString(R.string.master_password_verify), this, GetTextFragment.Password, R.string.master_password_verify_message, R.string.master_password_set_error); } if (getNewPassword == null) { getNewPassword = GetTextFragment.newInstance(getString(R.string.master_password_set), this, GetTextFragment.MatchingPasswordTwice, R.string.master_password_set_message, R.string.master_password_set_error); } textNickname = (EditText) findViewById(R.id.textNickname); spinnerConnection = (Spinner)findViewById(R.id.spinnerConnection); spinnerConnection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> ad, View view, int itemIndex, long id) { selected = (ConnectionBean)ad.getSelectedItem(); updateViewFromSelected(); } @Override public void onNothingSelected(AdapterView<?> ad) { selected = null; } }); // Here we say what happens when the Pubkey Generate button is pressed. buttonGeneratePubkey = (Button) findViewById(R.id.buttonGeneratePubkey); buttonGeneratePubkey.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { generatePubkey (); } }); database = ((App)getApplication()).getDatabase(); } @Override protected void onStart() { Log.i(TAG, "onStart called"); super.onStart(); System.gc(); } @Override protected void onResume() { Log.i(TAG, "onResume called"); super.onResume(); System.gc(); } @Override protected void onResumeFragments() { Log.i(TAG, "onResumeFragments called"); super.onResumeFragments(); System.gc(); if (Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)) { showGetTextFragment(getPassword); } else { arriveOnPage(); } } @Override public void onWindowFocusChanged (boolean visible) { } @Override public void onConfigurationChanged(Configuration newConfig) { Log.i(TAG, "onConfigurationChanged called"); super.onConfigurationChanged(newConfig); } @Override protected void onStop() { super.onStop(); Log.i(TAG, "onStop called"); if (database != null) database.close(); if ( selected == null ) { return; } } @Override protected void onPause() { super.onPause(); Log.i(TAG, "onPause called"); if (database != null) database.close(); if (!isConnecting) { startingOrHasPaused = true; } else { isConnecting = false; } updateSelectedFromView(); selected.saveAndWriteRecent(false, database); } @Override protected void onDestroy() { if (database != null) database.close(); System.gc(); super.onDestroy(); } protected void canvasStart() { if (selected == null) return; MemoryInfo info = Utils.getMemoryInfo(this); if (info.lowMemory) System.gc(); start(); } /** * Starts the activity which makes a VNC connection and displays the remote desktop. */ private void start() { isConnecting = true; updateSelectedFromView(); Intent intent = new Intent(this, RemoteCanvasActivity.class); intent.putExtra(Utils.getConnectionString(this), selected.Gen_getValues()); startActivity(intent); } public void arriveOnPage() { Log.i(TAG, "arriveOnPage called"); SQLiteDatabase db = database.getReadableDatabase(); ArrayList<ConnectionBean> connections = new ArrayList<ConnectionBean>(); ConnectionBean.getAll(db, ConnectionBean.GEN_TABLE_NAME, connections, ConnectionBean.newInstance); Collections.sort(connections); connections.add(0, new ConnectionBean(this)); int connectionIndex = 0; if (connections.size() > 1) { MostRecentBean mostRecent = ConnectionBean.getMostRecent(db); if (mostRecent != null) { for (int i = 1; i < connections.size(); ++i) { if (connections.get(i).get_Id() == mostRecent.getConnectionId()) { connectionIndex = i; break; } } } } database.close(); spinnerConnection.setAdapter(new ArrayAdapter<ConnectionBean>(this, R.layout.connection_list_entry, connections.toArray(new ConnectionBean[connections.size()]))); spinnerConnection.setSelection(connectionIndex, false); selected = connections.get(connectionIndex); updateViewFromSelected(); IntroTextDialog.showIntroTextIfNecessary(this, database, Utils.isFree(this) && startingOrHasPaused); startingOrHasPaused = false; } /** * Starts the activity which manages keys. */ protected void generatePubkey () { updateSelectedFromView(); selected.saveAndWriteRecent(true, database); Intent intent = new Intent(this, GeneratePubkeyActivity.class); intent.putExtra("PrivateKey",selected.getSshPrivKey()); startActivityForResult(intent, Constants.ACTIVITY_GEN_KEY); } public Database getDatabaseHelper() { return database; } /** * Returns the display height, or if the device has software * buttons, the 'bottom' of the view (in order to take into account the * software buttons. * @return the height in pixels. */ public int getHeight () { View v = getWindow().getDecorView().findViewById(android.R.id.content); Display d = getWindowManager().getDefaultDisplay(); int bottom = v.getBottom(); Point outSize = new Point(); d.getSize(outSize); int height = outSize.y; int value = height; if (android.os.Build.VERSION.SDK_INT >= 14) { android.view.ViewConfiguration vc = ViewConfiguration.get(this); if (vc.hasPermanentMenuKey()) value = bottom; } if (Utils.isBlackBerry ()) { value = bottom; } return value; } /** * Returns the display width, or if the device has software * buttons, the 'right' of the view (in order to take into account the * software buttons. * @return the width in pixels. */ public int getWidth () { View v = getWindow().getDecorView().findViewById(android.R.id.content); Display d = getWindowManager().getDefaultDisplay(); int right = v.getRight(); Point outSize = new Point(); d.getSize(outSize); int width = outSize.x; if (android.os.Build.VERSION.SDK_INT >= 14) { android.view.ViewConfiguration vc = ViewConfiguration.get(this); if (vc.hasPermanentMenuKey()) return right; } return width; } /* (non-Javadoc) * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.androidvncmenu, menu); return true; } /* (non-Javadoc) * @see android.app.Activity#onMenuOpened(int, android.view.Menu) */ @Override public boolean onMenuOpened(int featureId, Menu menu) { if (menu != null) { menu.findItem(R.id.itemDeleteConnection).setEnabled(selected != null && !selected.isNew()); menu.findItem(R.id.itemSaveAsCopy).setEnabled(selected != null && !selected.isNew()); MenuItem itemMasterPassword = menu.findItem(R.id.itemMasterPassword); itemMasterPassword.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)); MenuItem keepScreenOn = menu.findItem(R.id.itemKeepScreenOn); keepScreenOn.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.keepScreenOnTag)); MenuItem disableImmersive = menu.findItem(R.id.itemDisableImmersive); disableImmersive.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.disableImmersiveTag)); MenuItem forceLandscape = menu.findItem(R.id.itemForceLandscape); forceLandscape.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.forceLandscapeTag)); MenuItem rAltAsIsoL3Shift = menu.findItem(R.id.itemRAltAsIsoL3Shift); rAltAsIsoL3Shift.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.rAltAsIsoL3ShiftTag)); MenuItem itemLeftHandedMode = menu.findItem(R.id.itemLeftHandedMode); itemLeftHandedMode.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.leftHandedModeTag)); } return true; } /* (non-Javadoc) * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.itemSaveAsCopy: if (selected.getNickname().equals(textNickname.getText().toString())) textNickname.setText("Copy of "+selected.getNickname()); updateSelectedFromView(); selected.set_Id(0); selected.saveAndWriteRecent(false, database); arriveOnPage(); break; case R.id.itemDeleteConnection: Utils.showYesNoPrompt(this, "Delete?", "Delete " + selected.getNickname() + "?", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { selected.Gen_delete(database.getWritableDatabase()); database.close(); arriveOnPage(); } }, null); break; case R.id.itemMainScreenHelp: showDialog(R.id.itemMainScreenHelp); break; case R.id.itemExportImport: showDialog(R.layout.importexport); break; case R.id.itemMasterPassword: if (Utils.isFree(this)) { IntroTextDialog.showIntroTextIfNecessary(this, database, true); } else { togglingMasterPassword = true; if (Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)) { showGetTextFragment(getPassword); } else { showGetTextFragment(getNewPassword); } } break; case R.id.itemKeepScreenOn: Utils.toggleSharedPreferenceBoolean(this, Constants.keepScreenOnTag); break; case R.id.itemDisableImmersive: Utils.toggleSharedPreferenceBoolean(this, Constants.disableImmersiveTag); break; case R.id.itemForceLandscape: Utils.toggleSharedPreferenceBoolean(this, Constants.forceLandscapeTag); break; case R.id.itemRAltAsIsoL3Shift: Utils.toggleSharedPreferenceBoolean(this, Constants.rAltAsIsoL3ShiftTag); break; case R.id.itemLeftHandedMode: Utils.toggleSharedPreferenceBoolean(this, Constants.leftHandedModeTag); break; } return true; } private boolean checkMasterPassword (String password) { Log.i(TAG, "Checking master password."); boolean result = false; Database testPassword = new Database(this); testPassword.close(); try { testPassword.getReadableDatabase(password); result = true; } catch (Exception e) { result = false; } testPassword.close(); return result; } public void onTextObtained(String obtainedString, boolean wasCancelled) { handlePassword(obtainedString, wasCancelled); } public void handlePassword(String providedPassword, boolean wasCancelled) { if (togglingMasterPassword) { Log.i(TAG, "Asked to toggle master pasword."); // The user has requested the password to be enabled or disabled. togglingMasterPassword = false; if (Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)) { Log.i(TAG, "Master password is enabled."); // Master password is enabled if (wasCancelled) { Log.i(TAG, "Dialog cancelled, so quitting."); Utils.showFatalErrorMessage(this, getResources().getString(R.string.master_password_error_password_necessary)); } else if (checkMasterPassword(providedPassword)) { Log.i(TAG, "Entered password correct, disabling password."); // Disable the password since the user input the correct password. Database.setPassword(providedPassword); if (database.changeDatabasePassword("")) { Utils.toggleSharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag); } else { Utils.showErrorMessage(this, getResources().getString(R.string.master_password_error_failed_to_disable)); } removeGetPasswordFragments(); arriveOnPage(); } else { Log.i(TAG, "Entered password is wrong or dialog cancelled, so quitting."); Utils.showFatalErrorMessage(this, getResources().getString(R.string.master_password_error_wrong_password)); } } else { Log.i(TAG, "Master password is disabled."); if (!wasCancelled) { // The password is disabled, so set it in the preferences. Log.i(TAG, "Setting master password."); Database.setPassword(""); if (database.changeDatabasePassword(providedPassword)) { Utils.toggleSharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag); } else { Utils.showErrorMessage(this, getResources().getString(R.string.master_password_error_failed_to_enable)); } } else { // No need to show error message because user cancelled consciously. Log.i(TAG, "Dialog cancelled, not setting master password."); Utils.showErrorMessage(this, getResources().getString(R.string.master_password_error_password_not_set)); } removeGetPasswordFragments(); arriveOnPage(); } } else { // We are just trying to check the password. Log.i(TAG, "Just checking the password."); if (wasCancelled) { Log.i(TAG, "Dialog cancelled, so quitting."); Utils.showFatalErrorMessage(this, getResources().getString(R.string.master_password_error_password_necessary)); } else if (checkMasterPassword(providedPassword)) { Log.i(TAG, "Entered password is correct, so proceeding."); Database.setPassword(providedPassword); removeGetPasswordFragments(); arriveOnPage(); } else { // Finish the activity if the password was wrong. Log.i(TAG, "Entered password is wrong, so quitting."); Utils.showFatalErrorMessage(this, getResources().getString(R.string.master_password_error_wrong_password)); } } } private void showGetTextFragment(GetTextFragment f) { if (!f.isVisible()) { removeGetPasswordFragments(); FragmentManager fm = ((FragmentActivity)this).getSupportFragmentManager(); f.setCancelable(false); f.show(fm, ""); } } private void removeGetPasswordFragments() { if (getPassword.isAdded()) { FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction(); tx.remove(getPassword); tx.commit(); getSupportFragmentManager().executePendingTransactions(); } if (getNewPassword.isAdded()) { FragmentTransaction tx = this.getSupportFragmentManager().beginTransaction(); tx.remove(getNewPassword); tx.commit(); getSupportFragmentManager().executePendingTransactions(); } } /** * This function is used to retrieve data returned by activities started with startActivityForResult. */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode) { case (Constants.ACTIVITY_GEN_KEY): if (resultCode == Activity.RESULT_OK) { Bundle b = data.getExtras(); String privateKey = (String)b.get("PrivateKey"); if (!privateKey.equals(selected.getSshPrivKey()) && privateKey.length() != 0) Toast.makeText(getBaseContext(), "New key generated/imported successfully. Tap 'Generate/Export Key' " + " button to share, copy to clipboard, or export the public key now.", Toast.LENGTH_LONG).show(); selected.setSshPrivKey(privateKey); selected.setSshPubKey((String)b.get("PublicKey")); selected.saveAndWriteRecent(true, database); } else Log.i (TAG, "The user cancelled SSH key generation."); break; } } }