Java tutorial
/** * Tab Weave, Mozilla Weave client for Android * Copyright (C) 2010 Arjen Verstoep <terr@xs4all.nl> * * 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 2 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package nl.terr.tabweave; import info.elebescond.weave.exception.WeaveException; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.List; import javax.crypto.NoSuchPaddingException; import nl.terr.weave.Config; import nl.terr.weave.CryptoWeave; import nl.terr.weave.SyncWeave; import nl.terr.weave.impl.CryptoWeaveImpl; import nl.terr.weave.impl.SyncWeaveImpl; import nl.terr.weave.impl.UserWeaveImpl; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.AlertDialog; import android.app.ListActivity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.MatrixCursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import biz.source_code.base64Coder.Base64Coder; public class TabWeave extends ListActivity { // private TabWeaveDbAdapter mWeaveTabsDbAdapter; // private CryptoWeave mCryptoWeave; private UserWeaveImpl mUserWeave; private SyncWeaveImpl mSyncWeave; public static final int ACTIVITY_EDIT_SETTINGS = 1; byte[] bytePrivateKeyDecrypted; byte[] byteSymmetricKeyDecrypted; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override public void onStart() { super.onStart(); refreshTabList(); } public void fillData(List<JSONObject> lTabs) { // Create an array to specify the fields we want to display in the list (only TITLE) String[] from = new String[] { SyncWeave.KEY_TITLE, SyncWeave.KEY_URL, SyncWeave.KEY_ROWID }; // and an array of the fields we want to bind those fields to (in this case just text1) int[] to = new int[] { R.id.title, R.id.url }; int iBrowserInstances = lTabs.size(); MatrixCursor matrixCursor = new MatrixCursor(from); int iTabId = 0; // Show "No tabs" message TextView tvNoTabs = (TextView) findViewById(R.id.no_tabs); tvNoTabs.setVisibility(View.VISIBLE); try { for (int iWeaveBrowserInstance = 0; iWeaveBrowserInstance < iBrowserInstances; iWeaveBrowserInstance++) { int iNumberTabs = lTabs.get(iWeaveBrowserInstance).getJSONArray("tabs").length(); // Hide "No tabs" message if (iNumberTabs > 0) { tvNoTabs.setVisibility(View.INVISIBLE); } for (int iWeaveTab = 0; iWeaveTab < iNumberTabs; iWeaveTab++) { matrixCursor.newRow() .add(lTabs.get(iWeaveBrowserInstance).getJSONArray("tabs").getJSONObject(iWeaveTab) .getString("title")) .add(lTabs.get(iWeaveBrowserInstance).getJSONArray("tabs").getJSONObject(iWeaveTab) .getJSONArray("urlHistory").getString(0)) .add(iTabId); iTabId++; } } } catch (Exception e) { e.printStackTrace(); AlertDialog alert = createAlertDialog(this, e.getClass().toString(), e.getMessage()); alert.show(); } ListAdapter listAdapter = new SimpleCursorAdapter(this, // Context R.layout.tab_row, // Specify the row template to use (here, two columns bound to the two retrieved cursor rows). matrixCursor, from, to); setListAdapter(listAdapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); TextView viewText = (TextView) v.findViewById(R.id.url); Intent viewIntent = new Intent("android.intent.action.VIEW", Uri.parse(viewText.getText().toString())); startActivity(viewIntent); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { switch (item.getItemId()) { case R.id.menuSettings: showSettings(); return true; case R.id.menuRefresh: refreshTabList(); return true; } return super.onMenuItemSelected(featureId, item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); /*if(resultCode == RESULT_OK) { Bundle extras = intent.getExtras(); }*/ switch (requestCode) { case ACTIVITY_EDIT_SETTINGS: if (resultCode != RESULT_OK) { Log.d("ACTIVITY_EDIT_SETTINGS", "Result from ACTIVITY_EDIT_SETTINGS was not 'OK'"); } // Recalculte crypto keys if necessary //prepareCryptoKeys(); //refreshTabList(); break; } } /** * Checks if all the required preferences for getting the tab data * are present. * @throws IOException * @throws WeaveException * @throws ClientProtocolException */ private boolean checkPreferencesComplete() throws ClientProtocolException, WeaveException, IOException { Config mConfig = Config.getConfig(this); String sUsername = mConfig.getUsername(); String sPassword = mConfig.getPassword(); String sPassphrase = mConfig.getPassphrase(); String sSyncServerUrl = mConfig.getWeaveNode(); // Redirect the user to the settings panel if any of the credentials are missing if (sUsername == "" || sPassword == "" || sPassphrase == "") { return false; } else { if (sSyncServerUrl == "") { mUserWeave = new UserWeaveImpl(); mConfig.setWeaveNode(mUserWeave.getUserStorageNode(sUsername, null)); mConfig.commit(); } return true; } } private void showSettings() { Intent i = new Intent(this, TabWeaveSettingsActivity.class); startActivityForResult(i, ACTIVITY_EDIT_SETTINGS); } private void prepareCryptoKeys() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException, WeaveException, JSONException, IOException, InvalidKeySpecException { Config mConfig = Config.getConfig(this); mSyncWeave = new SyncWeaveImpl(mConfig.getWeaveNode(), mConfig.getUsername(), mConfig.getPassword(), mConfig.getPassphrase()); // Check settings storage for the user's private key if (mConfig.getPrivateKey() == "") { bytePrivateKeyDecrypted = mSyncWeave.getPrivateKey(); // Save the generated private key mConfig.setPrivateKey(new String(Base64Coder.encode(bytePrivateKeyDecrypted))); mConfig.commit(); } bytePrivateKeyDecrypted = Base64Coder.decode(mConfig.getPrivateKey()); if (mConfig.getSymmetricKey() == "") { byteSymmetricKeyDecrypted = mSyncWeave.getSymmetricKey(bytePrivateKeyDecrypted); mConfig.setSymmetricKey(new String(Base64Coder.encode(byteSymmetricKeyDecrypted))); mConfig.commit(); } byteSymmetricKeyDecrypted = Base64Coder.decode(mConfig.getSymmetricKey()); } public void refreshTabList() { try { if (checkPreferencesComplete()) { new RefreshTabList().execute(); } else { showSettings(); } } catch (Exception e) { AlertDialog alert = createAlertDialog(this, e.getClass().toString(), e.getMessage()); alert.show(); } } public AlertDialog createAlertDialog(Context context, String sTitle, String sMessage) { AlertDialog.Builder builder = new AlertDialog.Builder(TabWeave.this); builder.setTitle(sTitle).setMessage(sMessage).setCancelable(false) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); AlertDialog alert = builder.create(); return alert; } private class RefreshTabList extends AsyncTask<Void, String, List<JSONObject>> { private JSONObject oTabPayload; private String sTabCipherText; private byte[] byteTabCipherText; private byte[] byteTabCipherIV; private CryptoWeave mCryptoWeave = new CryptoWeaveImpl(); private TextView statusMessage; private Exception exception; @Override protected void onPreExecute() { // ImageView logoImageView = (ImageView)findViewById(R.id.weaveLogoImageView); setContentView(R.layout.loading_screen); statusMessage = (TextView) findViewById(R.id.statusMessage); } @Override protected List<JSONObject> doInBackground(Void... params) { Log.d("RefreshTabList", "Starting doInBackground()"); List<JSONObject> lTabs = new ArrayList<JSONObject>(); try { Config mConfig = Config.getConfig(TabWeave.this); // If keys are missing if (mConfig.getPrivateKey() == "" || mConfig.getSymmetricKey() == "") { publishProgress(getString(R.string.status_generating_crypto_keys)); } // Keys are not missing and can be loaded else { publishProgress(getString(R.string.status_loading_crypto_keys)); } // Retrieve or calculate the crypto keys prepareCryptoKeys(); publishProgress(getString(R.string.status_retrieving_tabs)); // Request all tabs objects JSONArray aCollection = mSyncWeave.getCollection("tabs?full=1"); JSONObject[] oTabs = new JSONObject[32]; publishProgress(getString(R.string.status_decrypting_data)); int iTabCount = aCollection.length(); if (iTabCount > oTabs.length) { iTabCount = oTabs.length; } for (int x = 0; x < iTabCount; x++) { //oTabs[x] = mSyncWeave.getItem("tabs", aCollection.getString(x)); // Only needed when not requesting ?full=1 oTabs[x] = aCollection.getJSONObject(x); // Decrypt the ciphertext of this tab oTabPayload = new JSONObject(oTabs[x].getString("payload")); byteTabCipherText = Base64Coder.decode(oTabPayload.getString("ciphertext")); byteTabCipherIV = Base64Coder.decode(oTabPayload.getString("IV")); sTabCipherText = new String(mCryptoWeave.decryptCipherText(byteSymmetricKeyDecrypted, byteTabCipherIV, byteTabCipherText)); lTabs.add(x, new JSONObject(sTabCipherText)); } } catch (HttpResponseException e) { if (e.getStatusCode() == 401) { exception = new HttpResponseException(401, getString(R.string.http_unauthorized)); } cancel(true); } catch (IOException e) { exception = new IOException(getString(R.string.passphrase_incorrect)); cancel(true); } catch (IllegalStateException e) { exception = new IllegalStateException(getString(R.string.no_server_info)); cancel(true); } catch (Exception e) { exception = e; cancel(true); } return lTabs; } @Override protected void onProgressUpdate(String... params) { statusMessage.setText(params[0]); } @Override protected void onPostExecute(List<JSONObject> lTabs) { setContentView(R.layout.main); if (exception != null) { exception.printStackTrace(); AlertDialog alert = createAlertDialog(TabWeave.this, exception.getClass().toString(), exception.getMessage()); alert.show(); } else { fillData(lTabs); } } } }