unknown intent action " + action);
- finish();
- return;
- }
-
- if (isSignedIn) {
- mBtnSignIn.setText(getString(R.string.menu_sign_out));
- mBtnSignIn.setBackgroundResource(R.drawable.btn_red);
- }
-
- final BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
-
- mRememberPass.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
-
- CheckBox mRememberPass = (CheckBox) v;
-
- if (mRememberPass.isChecked()) {
- String msg = brandingRes
- .getString(BrandingResourceIDs.STRING_TOAST_CHECK_SAVE_PASSWORD);
- Toast.makeText(CreateAccountActivity.this, msg, Toast.LENGTH_LONG).show();
- }
- }
- });
-
- mEditUserAccount.addTextChangedListener(mTextWatcher);
- mEditPass.addTextChangedListener(mTextWatcher);
-
- mBtnAdvanced.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- showAdvanced();
- }
- });
-
- mBtnDelete.setOnClickListener(new OnClickListener()
- {
-
- @Override
- public void onClick(View v) {
-
- deleteAccount();
- finish();
-
- }
-
- });
-
- mBtnSignIn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
-
- checkUserChanged();
-
- if (mUseTor.isChecked())
- {
- OrbotHelper oh = new OrbotHelper(CreateAccountActivity.this);
- if (!oh.isOrbotRunning())
- {
- oh.requestOrbotStart(CreateAccountActivity.this);
- return;
- }
- }
-
-
- final String pass = mEditPass.getText().toString();
- final boolean rememberPass = mRememberPass.isChecked();
- final boolean isActive = false; // TODO(miron) does this ever need to be true?
- ContentResolver cr = getContentResolver();
-
- if (!parseAccount(mEditUserAccount.getText().toString())) {
- mEditUserAccount.selectAll();
- mEditUserAccount.requestFocus();
- return;
- }
-
- final long accountId = ImApp.insertOrUpdateAccount(cr, mProviderId, mUserName,
- rememberPass ? pass : null);
-
- mAccountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
-
- //if remember pass is true, set the "keep signed in" property to true
-
- if (isSignedIn) {
- signOut();
- isSignedIn = false;
- } else {
- ContentValues values = new ContentValues();
- values.put(AccountColumns.KEEP_SIGNED_IN, rememberPass ? 1 : 0);
- getContentResolver().update(mAccountUri, values, null, null);
-
- if (!mOriginalUserAccount.equals(mUserName + '@' + mDomain)
- && shouldShowTermOfUse(brandingRes)) {
- confirmTermsOfUse(brandingRes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mSignInHelper.signIn(pass, mProviderId, accountId, isActive);
- }
- });
- } else {
- mSignInHelper.signIn(pass, mProviderId, accountId, isActive);
- }
- isSignedIn = true;
- }
- updateWidgetState();
-
- }
- });
-
- mUseTor.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- updateUseTor(isChecked);
- }
- });
-
- updateWidgetState();
-
-
- if (i.hasExtra("newuser"))
- {
- String newuser = i.getExtras().getString("newuser");
- mEditUserAccount.setText(newuser);
-
- parseAccount(newuser);
- settingsForDomain(mDomain,mPort);
-
- }
-
- if (i.hasExtra("newpass"))
- {
- mEditPass.setText(i.getExtras().getString("newpass"));
- mRememberPass.setChecked(true);
- }
-
-
- }
-
- @Override
- protected void onDestroy() {
-
- if (mSignInHelper != null)
- mSignInHelper.stop();
-
- if (settings != null)
- settings.close();
-
- super.onDestroy();
- }
-
- private void updateUseTor(boolean useTor) {
- checkUserChanged();
-
- final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
- getContentResolver(), mProviderId, false /* don't keep updated */, null /* no handler */);
-
- OrbotHelper orbotHelper = new OrbotHelper(this);
-
- if (useTor && (!orbotHelper.isOrbotInstalled()))
- {
- //Toast.makeText(this, "Orbot app is not installed. Please install from Google Play or from https://guardianproject.info/releases", Toast.LENGTH_LONG).show();
-
- orbotHelper.promptToInstall(this);
-
- mUseTor.setChecked(false);
- settings.setUseTor(false);
- settings.setServer("");
- }
- else
- {
-
- // if using Tor, disable DNS SRV to reduce anonymity leaks
- settings.setDoDnsSrv(!useTor);
-
- String server = settings.getServer();
-
- if (useTor && (server == null || server.length() == 0)) {
- server = settings.getDomain();
- String domain = settings.getDomain().toLowerCase();
-
- // a little bit of custom handling here
- if (domain.equals("gmail.com")) {
- server = "talk.l.google.com";
- } else if (domain.equals("jabber.ccc.de")) {
- server = "okj7xc6j2szr2y75.onion";
- } else if (domain.equals("jabber.org")) {
- server = "hermes.jabber.org";
- } else if (domain.equals("chat.facebook.com")) {
- server = "chat.facebook.com";
- } else if (domain.equals("dukgo.com")) {
- server = "dukgo.com";
- //settings.setTlsCertVerify(false); //remove this - MemorizingTrustManager will now prompt
- }
- else
- {
- Toast.makeText(this, getString(R.string.warning_tor_connect), Toast.LENGTH_LONG).show();
- }
-
- }
- else
- {
-
- }
-
- settings.setServer(server);
- settings.setUseTor(useTor);
- }
-
- settings.close();
-
- }
-
- private void getOTRKeyInfo() {
-
- if (mApp != null && mApp.getRemoteImService() != null) {
- try {
- otrKeyManager = mApp.getRemoteImService().getOtrKeyManager(mOriginalUserAccount);
-
- if (otrKeyManager == null) {
- mTxtFingerprint = ((TextView) findViewById(R.id.txtFingerprint));
-
- String localFingerprint = otrKeyManager.getLocalFingerprint();
- if (localFingerprint != null) {
- ((TextView) findViewById(R.id.lblFingerprint)).setVisibility(View.VISIBLE);
- mTxtFingerprint.setText(processFingerprint(localFingerprint));
- } else {
- ((TextView) findViewById(R.id.lblFingerprint)).setVisibility(View.GONE);
- mTxtFingerprint.setText("");
- }
- } else {
- //don't need to notify people if there is nothing to show here
-// Toast.makeText(this, "OTR is not initialized yet", Toast.LENGTH_SHORT).show();
- }
-
- } catch (Exception e) {
- Log.e(ImApp.LOG_TAG, "error on create", e);
-
- }
- }
-
- }
-
- private void checkUserChanged() {
- String username = mEditUserAccount.getText().toString().trim();
-
- if ((!username.equals(mOriginalUserAccount)) && parseAccount(username)) {
- //Log.i(TAG, "Username changed: " + mOriginalUserAccount + " != " + username);
- settingsForDomain(mDomain, mPort);
- mOriginalUserAccount = username;
-
- }
-
- }
-
-
-
- boolean parseAccount(String userField) {
- boolean isGood = true;
- String[] splitAt = userField.trim().split("@");
- mUserName = splitAt[0];
- mDomain = null;
- mPort = 0;
-
- if (splitAt.length > 1) {
- mDomain = splitAt[1].toLowerCase();
- String[] splitColon = mDomain.split(":");
- mDomain = splitColon[0];
- if (splitColon.length > 1) {
- try {
- mPort = Integer.parseInt(splitColon[1]);
- } catch (NumberFormatException e) {
- // TODO move these strings to strings.xml
- isGood = false;
- Toast.makeText(
- CreateAccountActivity.this,
- "The port value '" + splitColon[1]
- + "' after the : could not be parsed as a number!",
- Toast.LENGTH_LONG).show();
- }
- }
- }
-
- if (mDomain == null) {
- isGood = false;
- //Toast.makeText(AccountActivity.this,
- // R.string.account_wizard_no_domain_warning,
- // Toast.LENGTH_LONG).show();
- }
- /*//removing requirement of a . in the domain
- else if (mDomain.indexOf(".") == -1) {
- isGood = false;
- // Toast.makeText(AccountActivity.this,
- // R.string.account_wizard_no_root_domain_warning,
- // Toast.LENGTH_LONG).show();
- }*/
-
- return isGood;
- }
-
- void settingsForDomain(String domain, int port) {
-
-
- if (domain.equals("gmail.com")) {
- // Google only supports a certain configuration for XMPP:
- // http://code.google.com/apis/talk/open_communications.html
- settings.setDoDnsSrv(true);
- settings.setServer("");
- settings.setDomain(domain);
- settings.setPort(DEFAULT_PORT);
- settings.setRequireTls(true);
- settings.setTlsCertVerify(true);
- settings.setAllowPlainAuth(false);
- }
- else if (mEditPass.getText().toString().startsWith(GTalkOAuth2.NAME))
- {
- //this is not @gmail but IS a google account
- settings.setDoDnsSrv(false);
- settings.setServer("talk.google.com"); //set the google connect server
- settings.setDomain(domain);
- settings.setPort(DEFAULT_PORT);
- settings.setRequireTls(true);
- settings.setTlsCertVerify(true);
- settings.setAllowPlainAuth(false);
- }
- else if (domain.equals("jabber.org")) {
- settings.setDoDnsSrv(true);
- settings.setDomain(domain);
- settings.setPort(DEFAULT_PORT);
- settings.setServer("");
- settings.setRequireTls(true);
- settings.setTlsCertVerify(true);
- settings.setAllowPlainAuth(false);
- } else if (domain.equals("facebook.com")) {
- settings.setDoDnsSrv(false);
- settings.setDomain("chat.facebook.com");
- settings.setPort(DEFAULT_PORT);
- settings.setServer("chat.facebook.com");
- settings.setRequireTls(true); //facebook TLS now seems to be on
- settings.setTlsCertVerify(false); //but cert verify can still be funky - off by default
- settings.setAllowPlainAuth(false);
- } else {
- settings.setDoDnsSrv(true);
- settings.setDomain(domain);
- settings.setPort(port);
- settings.setServer("");
- settings.setRequireTls(true);
- settings.setTlsCertVerify(true);
- settings.setAllowPlainAuth(false);
- }
- }
-
- void confirmTermsOfUse(BrandingResources res, DialogInterface.OnClickListener accept) {
- SpannableString message = new SpannableString(
- res.getString(BrandingResourceIDs.STRING_TOU_MESSAGE));
- Linkify.addLinks(message, Linkify.ALL);
-
- new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(res.getString(BrandingResourceIDs.STRING_TOU_TITLE)).setMessage(message)
- .setPositiveButton(res.getString(BrandingResourceIDs.STRING_TOU_DECLINE), null)
- .setNegativeButton(res.getString(BrandingResourceIDs.STRING_TOU_ACCEPT), accept)
- .show();
- }
-
- boolean shouldShowTermOfUse(BrandingResources res) {
- return !TextUtils.isEmpty(res.getString(BrandingResourceIDs.STRING_TOU_MESSAGE));
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mAccountUri = savedInstanceState.getParcelable(ACCOUNT_URI_KEY);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(ACCOUNT_URI_KEY, mAccountUri);
- }
-
- void signOutUsingActivity() {
-
- Intent intent = new Intent(CreateAccountActivity.this, SignoutActivity.class);
- intent.setData(mAccountUri);
-
- startActivity(intent);
- }
-
- private Handler mHandler = new Handler();
- private ImApp mApp = null;
-
- void signOut() {
- //if you are signing out, then we will deactive "auto" sign in
- ContentValues values = new ContentValues();
- values.put(AccountColumns.KEEP_SIGNED_IN, 0);
- getContentResolver().update(mAccountUri, values, null, null);
-
- mApp = (ImApp)getApplication();
-
- mApp.callWhenServiceConnected(mHandler, new Runnable() {
- @Override
- public void run() {
-
- signOut(mProviderId, mAccountId);
- }
- });
-
- }
-
- void signOut(long providerId, long accountId) {
-
- try {
-
- IImConnection conn = mApp.getConnection(providerId);
- if (conn != null) {
- conn.logout();
- } else {
- // Normally, we can always get the connection when user chose to
- // sign out. However, if the application crash unexpectedly, the
- // status will never be updated. Clear the status in this case
- // to make it recoverable from the crash.
- ContentValues values = new ContentValues(2);
- values.put(AccountStatusColumns.PRESENCE_STATUS, CommonPresenceColumns.OFFLINE);
- values.put(AccountStatusColumns.CONNECTION_STATUS, Imps.ConnectionStatus.OFFLINE);
- String where = AccountStatusColumns.ACCOUNT + "=?";
- getContentResolver().update(Imps.AccountStatus.CONTENT_URI, values, where,
- new String[] { Long.toString(accountId) });
- }
- } catch (RemoteException ex) {
- Log.e(ImApp.LOG_TAG, "signout: caught ", ex);
- } finally {
-
- Toast.makeText(this,
- getString(R.string.signed_out_prompt, this.mEditUserAccount.getText()),
- Toast.LENGTH_SHORT).show();
- isSignedIn = false;
-
- mBtnSignIn.setText(getString(R.string.sign_in));
- mBtnSignIn.setBackgroundResource(R.drawable.btn_green);
- }
- }
-
- void createNewaccount (long accountId)
- {
-
- ContentValues values = new ContentValues(2);
-
- values.put(AccountStatusColumns.PRESENCE_STATUS, CommonPresenceColumns.NEW_ACCOUNT);
- values.put(AccountStatusColumns.CONNECTION_STATUS, Imps.ConnectionStatus.OFFLINE);
- String where = AccountStatusColumns.ACCOUNT + "=?";
- getContentResolver().update(Imps.AccountStatus.CONTENT_URI, values, where,
- new String[] { Long.toString(accountId) });
-
-
-
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
-
- if (requestCode == REQUEST_SIGN_IN) {
- if (resultCode == RESULT_OK) {
-
- finish();
- } else {
- // sign in failed, let's show the screen!
- }
- }
- }
-
- void updateWidgetState() {
- boolean goodUsername = mEditUserAccount.getText().length() > 0;
- boolean goodPassword = mEditPass.getText().length() > 0;
- boolean hasNameAndPassword = goodUsername && goodPassword;
-
- mEditPass.setEnabled(goodUsername);
- mEditPass.setFocusable(goodUsername);
- mEditPass.setFocusableInTouchMode(goodUsername);
-
- // enable keep sign in only when remember password is checked.
- boolean rememberPass = mRememberPass.isChecked();
- if (rememberPass && !hasNameAndPassword) {
- mRememberPass.setChecked(false);
- rememberPass = false;
-
- }
- mRememberPass.setEnabled(hasNameAndPassword);
- mRememberPass.setFocusable(hasNameAndPassword);
-
- mEditUserAccount.setEnabled(!isSignedIn);
- mEditPass.setEnabled(!isSignedIn);
-
- if (!isSignedIn) {
- mBtnSignIn.setEnabled(hasNameAndPassword);
- mBtnSignIn.setFocusable(hasNameAndPassword);
- }
- }
-
- private final TextWatcher mTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int before, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int after) {
- updateWidgetState();
-
- }
-
- @Override
- public void afterTextChanged(Editable s) {
-
- }
- };
-
- private void deleteAccount ()
- {
- Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, mAccountId);
- getContentResolver().delete(accountUri, null, null);
- Uri providerUri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, mProviderId);
- getContentResolver().delete(providerUri, null, null);
-
- }
-
- private void showAdvanced() {
-
- checkUserChanged();
-
- Intent intent = new Intent(this, AccountSettingsActivity.class);
- intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId);
- startActivity(intent);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getSupportMenuInflater();
- inflater.inflate(R.menu.account_settings_menu, menu);
-
- if (isEdit) {
- //add delete menu option
- }
-
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- finish();
- return true;
-
- case R.id.menu_gen_key:
- otrGenKey();
- return true;
-
-
- }
- return super.onOptionsItemSelected(item);
- }
-
- ProgressDialog pbarDialog;
-
- private void otrGenKey() {
-
- pbarDialog = new ProgressDialog(this);
-
- pbarDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- pbarDialog.setMessage(getString(R.string.otr_gen_key));
- pbarDialog.show();
-
- KeyGenThread kgt = new KeyGenThread();
- kgt.start();
-
- }
-
- private class KeyGenThread extends Thread {
-
- public KeyGenThread() {
-
- }
-
- @Override
- public void run() {
-
- try {
- if (otrKeyManager != null) {
- otrKeyManager.generateLocalKeyPair();
-
- } else {
- Toast.makeText(CreateAccountActivity.this, "OTR is not initialized yet",
- Toast.LENGTH_SHORT).show();
- }
- } catch (Exception e) {
- Log.e("OTR", "could not gen local key pair", e);
- } finally {
- handler.sendEmptyMessage(0);
- }
-
- }
-
- private Handler handler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
-
- pbarDialog.dismiss();
-
- try {
- if (otrKeyManager != null) {
- String lFingerprint = otrKeyManager.getLocalFingerprint();
- mTxtFingerprint.setText(processFingerprint(lFingerprint));
- }
-
- } catch (Exception e) {
- Log.e("OTR", "could not gen local key pair", e);
- }
-
- }
- };
- }
-
- private String processFingerprint(String fingerprint) {
- StringBuffer out = new StringBuffer();
-
- for (int n = 0; n < fingerprint.length(); n++) {
- for (int i = n; n < i + 4; n++) {
- out.append(fingerprint.charAt(n));
- }
-
- out.append(' ');
- }
-
- return out.toString();
- }
-
-
-
-
-}
diff --git a/src/info/guardianproject/otr/app/im/app/DatabaseUtils.java b/src/info/guardianproject/otr/app/im/app/DatabaseUtils.java
index bf609a91a..4e1f7c635 100644
--- a/src/info/guardianproject/otr/app/im/app/DatabaseUtils.java
+++ b/src/info/guardianproject/otr/app/im/app/DatabaseUtils.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -19,17 +19,20 @@
import info.guardianproject.otr.app.im.plugin.ImConfigNames;
import info.guardianproject.otr.app.im.provider.Imps;
+import info.guardianproject.otr.app.im.ui.RoundedAvatarDrawable;
+import java.io.ByteArrayOutputStream;
import java.util.Map;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
@@ -52,58 +55,53 @@ public static Cursor queryAccountsForProvider(ContentResolver cr, String[] proje
return c;
}
- public static Drawable getAvatarFromCursor(Cursor cursor, int dataColumn, int width, int height) {
- byte[] rawData = cursor.getBlob(dataColumn);
- if (rawData == null) {
+ public static RoundedAvatarDrawable getAvatarFromCursor(Cursor cursor, int dataColumn, int width, int height) throws DecoderException {
+ String hexData = cursor.getString(dataColumn);
+ if (hexData.equals("NULL")) {
return null;
}
- return decodeAvatar(rawData, width, height);
- }
- public static Uri getAvatarUri(Uri baseUri, long providerId, long accountId) {
- Uri.Builder builder = baseUri.buildUpon();
- ContentUris.appendId(builder, providerId);
- ContentUris.appendId(builder, accountId);
- return builder.build();
+ byte[] data = Hex.decodeHex(hexData.substring(2, hexData.length() - 1).toCharArray());
+ return decodeAvatar(data, width, height);
}
- public static Drawable getAvatarFromCursor(Cursor cursor, int dataColumn,
- int encodedDataColumn, String username, boolean updateBlobUseCursor,
- ContentResolver resolver, Uri updateBlobUri, int width, int height) {
- /**
- * Optimization: the avatar table in IM content provider have two
- * columns, one for the raw blob data, another for the base64 encoded
- * data. The reason for this is when the avatars are initially
- * downloaded, they are in the base64 encoded form, and instead of
- * base64 decode the avatars for all the buddies up front, we can just
- * simply store the encoded data in the table, and decode them on demand
- * when displaying them. Once we decode the avatar, we store the decoded
- * data as a blob, and null out the encoded column in the avatars table.
- * query the raw blob data first, if present, great; if not, query the
- * encoded data, decode it and store as the blob, and null out the
- * encoded column.
- */
- byte[] rawData = cursor.getBlob(dataColumn);
-
- if (rawData == null) {
- String encodedData = cursor.getString(encodedDataColumn);
- if (encodedData == null) {
- // Log.e(LogTag.LOG_TAG, "getAvatarFromCursor for " + username +
- // ", no raw or encoded data!");
+ public static RoundedAvatarDrawable getAvatarFromAddress(ContentResolver cr, String address, int width, int height) throws DecoderException {
+
+ String[] projection = {Imps.Contacts.AVATAR_DATA};
+ String[] args = {address};
+ String query = Imps.Contacts.USERNAME + " LIKE ?";
+ Cursor cursor = cr.query(Imps.Contacts.CONTENT_URI,projection,
+ query, args, Imps.Contacts.DEFAULT_SORT_ORDER);
+
+ if (cursor.moveToFirst())
+ {
+ String hexData = cursor.getString(0);
+ cursor.close();
+ if (hexData.equals("NULL")) {
return null;
}
- if (updateBlobUseCursor) {
- }
- else {
- updateAvatarBlob(resolver, updateBlobUri, rawData, username);
- }
+ byte[] data = Hex.decodeHex(hexData.substring(2, hexData.length() - 1).toCharArray());
+
+ return decodeAvatar(data, width, height);
}
+ else
+ {
- return decodeAvatar(rawData, width, height);
+ cursor.close();
+ return null;
+ }
}
- public static void updateAvatarBlob(ContentResolver resolver, Uri updateUri, byte[] data,
+
+ public static Uri getAvatarUri(Uri baseUri, long providerId, long accountId) {
+ Uri.Builder builder = baseUri.buildUpon();
+ ContentUris.appendId(builder, providerId);
+ ContentUris.appendId(builder, accountId);
+ return builder.build();
+ }
+
+ public static void updateAvatarBlob(ContentResolver resolver, Uri updateUri, byte[] data,
String username) {
ContentValues values = new ContentValues(3);
values.put(Imps.Avatars.DATA, data);
@@ -114,10 +112,10 @@ public static void updateAvatarBlob(ContentResolver resolver, Uri updateUri, byt
String[] selectionArgs = new String[] { username };
resolver.update(updateUri, values, buf.toString(), selectionArgs);
-
+
}
-
- public static boolean hasAvatarContact(ContentResolver resolver, Uri updateUri,
+
+ public static boolean hasAvatarContact(ContentResolver resolver, Uri updateUri,
String username) {
ContentValues values = new ContentValues(3);
values.put(Imps.Avatars.CONTACT, username);
@@ -128,12 +126,33 @@ public static boolean hasAvatarContact(ContentResolver resolver, Uri updateUri,
String[] selectionArgs = new String[] { username };
return resolver.update(updateUri, values, buf.toString(), selectionArgs) > 0;
-
+
+ }
+
+ public static boolean doesAvatarHashExist(ContentResolver resolver, Uri queryUri,
+ String jid, String hash) {
+
+ StringBuilder buf = new StringBuilder(Imps.Avatars.CONTACT);
+ buf.append("=?");
+ buf.append(" AND ");
+ buf.append(Imps.Avatars.HASH);
+ buf.append("=?");
+
+ String[] selectionArgs = new String[] { jid, hash };
+
+ Cursor cursor = resolver.query(queryUri, null, buf.toString(), selectionArgs, null);
+ if (cursor == null)
+ return false;
+ try {
+ return cursor.getCount() > 0;
+ } finally {
+ cursor.close();
+ }
}
-
+
public static void insertAvatarBlob(ContentResolver resolver, Uri updateUri, long providerId, long accountId, byte[] data, String hash,
String contact) {
-
+
ContentValues values = new ContentValues(3);
values.put(Imps.Avatars.DATA, data);
values.put(Imps.Avatars.CONTACT, contact);
@@ -142,21 +161,28 @@ public static void insertAvatarBlob(ContentResolver resolver, Uri updateUri, lon
values.put(Imps.Avatars.HASH, hash);
resolver.insert(updateUri, values);
+
+
}
-
- private static Drawable decodeAvatar(byte[] data, int width, int height) {
-
+
+ private static RoundedAvatarDrawable decodeAvatar(byte[] data, int width, int height) {
+
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeByteArray(data, 0, data.length,options);
+ BitmapFactory.decodeByteArray(data, 0, data.length,options);
options.inSampleSize = calculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false;
- Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length,options);
- Drawable avatar = new BitmapDrawable(b);
- return avatar;
+ Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length,options);
+ if (b != null)
+ {
+ RoundedAvatarDrawable avatar = new RoundedAvatarDrawable(b);
+ return avatar;
+ }
+ else
+ return null;
}
-
+
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
@@ -181,7 +207,7 @@ public static int calculateInSampleSize(
/**
* Update IM provider database for a plugin using newly loaded information.
- *
+ *
* @param cr the resolver
* @param providerName the plugin provider name
* @param providerFullName the full name
diff --git a/src/info/guardianproject/otr/app/im/app/DummyActivity.java b/src/info/guardianproject/otr/app/im/app/DummyActivity.java
new file mode 100644
index 000000000..17f9d8a66
--- /dev/null
+++ b/src/info/guardianproject/otr/app/im/app/DummyActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
+ * Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package info.guardianproject.otr.app.im.app;
+
+import info.guardianproject.otr.app.im.R;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+public class DummyActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.w(ImApp.LOG_TAG, "DummyActivity launched by swipe");
+ super.onCreate(savedInstanceState);
+ ((ImApp)getApplication()).maybeInit(this);
+ finish();
+ }
+
+ // Unused for now
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ void showDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.im_label)
+ .setMessage(R.string.swipe_alert)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ }
+ });
+ if (Build.VERSION.SDK_INT >= 17)
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+ });
+ builder.show();
+ }
+}
diff --git a/src/info/guardianproject/otr/app/im/app/DynamicPagerAdapter.java b/src/info/guardianproject/otr/app/im/app/DynamicPagerAdapter.java
new file mode 100644
index 000000000..800bf8d26
--- /dev/null
+++ b/src/info/guardianproject/otr/app/im/app/DynamicPagerAdapter.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package info.guardianproject.otr.app.im.app;
+
+import java.util.ArrayList;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.PagerAdapter;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Implementation of {@link android.support.v4.view.PagerAdapter} that
+ * uses a {@link Fragment} to manage each page. This class also handles
+ * saving and restoring of fragment's state.
+ *
+ * This version of the pager is more useful when there are a large number
+ * of pages, working more like a list view. When pages are not visible to
+ * the user, their entire fragment may be destroyed, only keeping the saved
+ * state of that fragment. This allows the pager to hold on to much less
+ * memory associated with each visited page as compared to
+ * {@link FragmentPagerAdapter} at the cost of potentially more overhead when
+ * switching between pages.
+ *
+ *
When using FragmentPagerAdapter the host ViewPager must have a
+ * valid ID set.
+ *
+ * Subclasses only need to implement {@link #getItem(int)}
+ * and {@link #getCount()} to have a working adapter.
+ *
+ *
Here is an example implementation of a pager containing fragments of
+ * lists:
+ *
+ * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java
+ * complete}
+ *
+ *
The R.layout.fragment_pager resource of the top-level fragment is:
+ *
+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager.xml
+ * complete}
+ *
+ *
The R.layout.fragment_pager_list resource containing each
+ * individual fragment's layout is:
+ *
+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml
+ * complete}
+ */
+public abstract class DynamicPagerAdapter extends PagerAdapter {
+ private static final String TAG = "FragmentStatePagerAdapter";
+ private static final boolean DEBUG = false;
+
+ private final FragmentManager mFragmentManager;
+ private FragmentTransaction mCurTransaction = null;
+
+ private ArrayList mSavedState = new ArrayList();
+ private ArrayList mFragments = new ArrayList();
+ private Fragment mCurrentPrimaryItem = null;
+
+ public DynamicPagerAdapter(FragmentManager fm) {
+ mFragmentManager = fm;
+ }
+
+ /**
+ * Return the Fragment associated with a specified position.
+ */
+ public abstract Fragment getItem(int position);
+
+ public Fragment getItemAt(int position) {
+ if (position >= mFragments.size())
+ return null;
+ return mFragments.get(position);
+ }
+
+ @Override
+ public void startUpdate(ViewGroup container) {
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ // Fragments may have moved. Regenerate the fragment list.
+ ArrayList old = mFragments;
+ ArrayList oldState = mSavedState;
+ mFragments = new ArrayList();
+ mSavedState = new ArrayList();
+ for (int i = 0 ; i < old.size() ; i++) {
+ Fragment frag = old.get(i);
+ if (frag != null) {
+ int position = getItemPosition(frag);
+ if (position == POSITION_NONE)
+ continue;
+ if (position == POSITION_UNCHANGED)
+ position = i;
+ while (mFragments.size() <= position) {
+ mFragments.add(null);
+ mSavedState.add(null);
+ }
+ mFragments.set(position, frag);
+ if (oldState.size() > i)
+ mSavedState.set(position, oldState.get(i));
+ }
+ }
+
+ // The list must never shrink because other methods depend on it
+ while (mFragments.size() < old.size()) {
+ mFragments.add(null);
+ mSavedState.add(null);
+ }
+
+ super.notifyDataSetChanged();
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ // If we already have this item instantiated, there is nothing
+ // to do. This can happen when we are restoring the entire pager
+ // from its saved state, where the fragment manager has already
+ // taken care of restoring the fragments we previously had instantiated.
+ if (mFragments.size() > position) {
+ Fragment f = mFragments.get(position);
+ if (f != null) {
+ return f;
+ }
+ }
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ Fragment fragment = getItem(position);
+ if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
+ if (mSavedState.size() > position) {
+ Fragment.SavedState fss = mSavedState.get(position);
+ if (fss != null) {
+ fragment.setInitialSavedState(fss);
+ }
+ }
+ while (mFragments.size() <= position) {
+ mFragments.add(null);
+ }
+
+ fragment.setMenuVisibility(false);
+ fragment.setUserVisibleHint(false);
+ mFragments.set(position, fragment);
+ mCurTransaction.add(container.getId(), fragment);
+
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int _position, Object object) {
+ Fragment fragment = (Fragment)object;
+ // The supplied position is unreliable. When an item is deleted, the pre-reorg position is supplied,
+ // but when an item is scrolled off screen due to an insert, the post-reorg position is supplied.
+ // Find the item ourselves.
+ int position = mFragments.indexOf(fragment);
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ // Fragment might already have been reorged out of the list
+ if (position >= 0)
+ {
+ if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ + " v=" + ((Fragment)object).getView());
+ while (mSavedState.size() <= position) {
+ mSavedState.add(null);
+ }
+ mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+ mFragments.set(position, null);
+ }
+
+ // TODO do we need to unset the visible hint, etc.?
+ if (mCurrentPrimaryItem == fragment)
+ mCurrentPrimaryItem = null;
+
+ mCurTransaction.remove(fragment);
+ }
+
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment)object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ mCurrentPrimaryItem = fragment;
+ if (fragment != null) {
+ fragment.setMenuVisibility(true);
+ fragment.setUserVisibleHint(true);
+ }
+ }
+ }
+
+ @Override
+ public void finishUpdate(ViewGroup container) {
+ if (mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ mFragmentManager.executePendingTransactions();
+ }
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((Fragment)object).getView() == view;
+ }
+
+ @Override
+ public Parcelable saveState() {
+ Bundle state = null;
+// FIXME don't save internal fragment state for now, until we figure out classloader issue
+// if (mSavedState.size() > 0) {
+// state = new Bundle();
+// Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
+// mSavedState.toArray(fss);
+// state.putParcelableArray("states", fss);
+// }
+ for (int i=0; i keys = bundle.keySet();
+ for (String key: keys) {
+ if (key.startsWith("f")) {
+ int index = Integer.parseInt(key.substring(1));
+ Fragment f = mFragmentManager.getFragment(bundle, key);
+ if (f != null) {
+ while (mFragments.size() <= index) {
+ mFragments.add(null);
+ }
+ f.setMenuVisibility(false);
+ mFragments.set(index, f);
+ } else {
+ Log.w(TAG, "Bad fragment at key " + key);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/info/guardianproject/otr/app/im/app/ErrorResUtils.java b/src/info/guardianproject/otr/app/im/app/ErrorResUtils.java
index 74e7e569c..d02d34ad9 100644
--- a/src/info/guardianproject/otr/app/im/app/ErrorResUtils.java
+++ b/src/info/guardianproject/otr/app/im/app/ErrorResUtils.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/ExitActivity.java b/src/info/guardianproject/otr/app/im/app/ExitActivity.java
new file mode 100644
index 000000000..86a01c1de
--- /dev/null
+++ b/src/info/guardianproject/otr/app/im/app/ExitActivity.java
@@ -0,0 +1,36 @@
+
+package info.guardianproject.otr.app.im.app;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+
+public class ExitActivity extends Activity {
+
+ @SuppressLint("NewApi")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (Build.VERSION.SDK_INT >= 21) {
+ finishAndRemoveTask();
+ } else {
+ finish();
+ }
+
+ System.exit(0);
+ }
+
+ public static void exitAndRemoveFromRecentApps(Activity activity) {
+ Intent intent = new Intent(activity, ExitActivity.class);
+
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ activity.startActivity(intent);
+ }
+}
diff --git a/src/info/guardianproject/otr/app/im/app/ImApp.java b/src/info/guardianproject/otr/app/im/app/ImApp.java
index 4c2d3a9a6..2bc40fd35 100644
--- a/src/info/guardianproject/otr/app/im/app/ImApp.java
+++ b/src/info/guardianproject/otr/app/im/app/ImApp.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,8 +17,8 @@
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.cacheword.CacheWordActivityHandler;
-import info.guardianproject.cacheword.SQLCipherOpenHelper;
+import info.guardianproject.cacheword.PRNGFixes;
+import info.guardianproject.iocipher.VirtualFileSystem;
import info.guardianproject.otr.app.Broadcaster;
import info.guardianproject.otr.app.im.IChatSession;
import info.guardianproject.otr.app.im.IChatSessionManager;
@@ -32,13 +32,14 @@
import info.guardianproject.otr.app.im.plugin.BrandingResourceIDs;
import info.guardianproject.otr.app.im.plugin.ImPlugin;
import info.guardianproject.otr.app.im.plugin.ImPluginInfo;
+import info.guardianproject.otr.app.im.plugin.xmpp.XMPPCertPins;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
import info.guardianproject.util.AssetUtil;
-import info.guardianproject.util.LogCleaner;
-import info.guardianproject.util.PRNGFixes;
+import info.guardianproject.util.Debug;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -46,6 +47,14 @@
import java.util.Map;
import java.util.Properties;
+import net.hockeyapp.android.CrashManager;
+import net.hockeyapp.android.CrashManagerListener;
+import net.sqlcipher.database.SQLiteDatabase;
+
+import org.thoughtcrime.ssl.pinning.PinningTrustManager;
+import org.thoughtcrime.ssl.pinning.SystemKeyStore;
+
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
@@ -62,18 +71,25 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
+import de.duenndns.ssl.MemorizingTrustManager;
public class ImApp extends Application {
-
+
public static final String LOG_TAG = "GB.ImApp";
public static final String EXTRA_INTENT_SEND_TO_USER = "Send2_U";
@@ -92,12 +108,20 @@ public class ImApp extends Application {
public static final String HOCKEY_APP_ID = "2fa3b9252319e47367f1f125bb3adcd1";
public static final String DEFAULT_TIMEOUT_CACHEWORD = "-1"; //one day
-
+
public static final String CACHEWORD_PASSWORD_KEY = "pkey";
-
+ public static final String CLEAR_PASSWORD_KEY = "clear_key";
+
+ public static final String NO_CREATE_KEY = "nocreate";
+
+ //ACCOUNT SETTINGS Imps defaults
+ public static final String DEFAULT_XMPP_RESOURCE = "ChatSecure";
+ public static final int DEFAULT_XMPP_PRIORITY = 20;
+ public static final String DEFAULT_XMPP_OTR_MODE = "auto";
+
private Locale locale = null;
- private static ImApp sImApp;
+ public static ImApp sImApp;
IRemoteImService mImService;
@@ -107,6 +131,10 @@ public class ImApp extends Application {
Broadcaster mBroadcaster;
+ public MemorizingTrustManager mTrustManager;
+
+ public static boolean mUsingCacheword = false;
+
/**
* A queue of messages that are waiting to be sent when service is
* connected.
@@ -180,7 +208,7 @@ public void initialize(Activity activity) {
sImApp.onCreate();
}
*/
-
+
@Override
public Resources getResources() {
if (mApplicationContext == this) {
@@ -199,12 +227,6 @@ public ContentResolver getContentResolver() {
return mApplicationContext.getContentResolver();
}
- public ImApp() {
- super();
- mConnections = new HashMap();
- mApplicationContext = this;
- sImApp = this;
- }
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -215,7 +237,7 @@ public void onConfigurationChanged(Configuration newConfig) {
// object causes an infinite relaunch loop in Android 4.2 (JB MR1)
Configuration myConfig = new Configuration(newConfig);
myConfig.locale = locale;
-
+
Locale.setDefault(locale);
getResources().updateConfiguration(myConfig, getResources().getDisplayMetrics());
}
@@ -224,111 +246,169 @@ public void onConfigurationChanged(Configuration newConfig) {
@Override
public void onCreate() {
super.onCreate();
-
+
+ sImApp = this;
+
+ Debug.onAppStart();
+
PRNGFixes.apply(); //Google's fix for SecureRandom bug: http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
-
+
+ // load these libs up front to shorten the delay after typing the passphrase
+ SQLiteDatabase.loadLibs(getApplicationContext());
+ VirtualFileSystem.get().isMounted();
+
+ mConnections = new HashMap();
+ mApplicationContext = this;
+
+ initTrustManager();
+
mBroadcaster = new Broadcaster();
- setAppTheme(null);
-
+ setAppTheme(null,null);
+
checkLocale();
}
+
+ private boolean mThemeDark = false;
+
+ public boolean isThemeDark ()
+ {
+ return mThemeDark;
+ }
public void setAppTheme (Activity activity)
{
- /*
-
+ setAppTheme(activity, null);
+ }
+
+ private final static int COLOR_TOOLBAR_DARK = Color.parseColor("#263238");
+ private final static int COLOR_TOOLBAR_LIGHT = Color.parseColor("#B0BEC5");
+
+ public void setAppTheme (Activity activity, Toolbar toolbar)
+ {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
- //int themeId = settings.getInt("theme", R.style.Theme_Gibberbot_Light);
- boolean themeDark = settings.getBoolean("themeDark", false);
-
- if (themeDark)
- {
- setTheme(R.style.Theme_Sherlock);
-
+
+ mThemeDark = settings.getBoolean("themeDark", false);
+
+ if (mThemeDark)
+ {
+ setTheme(R.style.AppThemeDark);
+
+
if (activity != null)
- activity.setTheme(R.style.Theme_Sherlock);
+ {
+ activity.setTheme(R.style.AppThemeDark);
+ if (activity instanceof ActionBarActivity)
+ {
+ ActionBar ab = ((ActionBarActivity)activity).getSupportActionBar();
+
+ if (ab != null)
+ {
+ ab.setBackgroundDrawable(new ColorDrawable(COLOR_TOOLBAR_DARK));
+ }
+ }
+ }
+ if (toolbar != null)
+ toolbar.setBackgroundColor(COLOR_TOOLBAR_DARK);
+
}
else
{
- setTheme(R.style.Theme_Chatsecure);
+ setTheme(R.style.AppTheme);
+
+
+ if (activity != null)
+ {
+ activity.setTheme(R.style.AppTheme);
+ if (activity instanceof ActionBarActivity)
+ {
+ ActionBar ab = ((ActionBarActivity)activity).getSupportActionBar();
+
+ if (ab != null)
+ {
+ ab.setBackgroundDrawable(new ColorDrawable(COLOR_TOOLBAR_LIGHT));
+ }
+ }
+ }
+
+ if (toolbar != null)
+ toolbar.setBackgroundColor(COLOR_TOOLBAR_LIGHT);
- if (activity != null)
- activity.setTheme(R.style.Theme_Chatsecure);
}
-
+
Configuration config = getResources().getConfiguration();
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
- */
-
+
+ if (mImService != null)
+ {
+ boolean debugOn = settings.getBoolean("prefDebug", false);
+ try {
+ mImService.enableDebugLogging(debugOn);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
}
-
- public boolean checkLocale ()
+
+ public void checkLocale ()
{
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
Configuration config = getResources().getConfiguration();
- String lang = settings.getString(getString(R.string.pref_default_locale), "");
-
-
+ String lang = settings.getString(getString(R.string.key_default_locale_pref), "");
+
+
if ("".equals(lang)) {
- Properties props = AssetUtil.getProperties("gibberbot.properties", this);
+ Properties props = AssetUtil.getProperties("chatsecure.properties", this);
if (props != null) {
String configuredLocale = props.getProperty("locale");
if (configuredLocale != null && !"CHOOSE".equals(configuredLocale)) {
lang = configuredLocale;
Editor editor = settings.edit();
- editor.putString(getString(R.string.pref_default_locale), lang);
- editor.commit();
+ editor.putString(getString(R.string.key_default_locale_pref), lang);
+ editor.apply();
}
}
}
-
- boolean updatedLocale = false;
-
+
if (!"".equals(lang) && !config.locale.getLanguage().equals(lang)) {
- locale = new Locale(lang);
+ locale = new Locale(lang);
config.locale = locale;
+ Locale.setDefault(locale);
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
- updatedLocale = true;
}
loadDefaultBrandingRes();
-
- return updatedLocale;
}
- public boolean setNewLocale(Context context, String localeString) {
-
- /*
- Locale locale = new Locale(localeString);
-
- Configuration config = context.getResources().getConfiguration();
- config.locale = locale;
-
- context.getResources().updateConfiguration(config,
- context.getResources().getDisplayMetrics());
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ public void setNewLocale(Context context, String language) {
+ /* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */
+ String localeSplit[] = language.split("_");
+ if (localeSplit.length > 1)
+ locale = new Locale(localeSplit[0], localeSplit[1]);
+ else
+ locale = new Locale(language);
+ Configuration config = getResources().getConfiguration();
+ if (Build.VERSION.SDK_INT >= 17)
+ config.setLocale(locale);
+ else
+ config.locale = locale;
+ Locale.setDefault(locale);
+ getResources().updateConfiguration(config, getResources().getDisplayMetrics());
- Log.d(LOG_TAG, "locale = " + locale.getDisplayName());
- */
-
+ /* Set the preference after setting the locale in case something goes
+ wrong. If setting the locale causes an Exception, it should be set in the
+ preferences, otherwise ChatSecure will be stuck in a crash loop. */
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Editor prefEdit = prefs.edit();
- prefEdit.putString(context.getString(R.string.pref_default_locale), localeString);
- prefEdit.commit();
-
- Configuration config = getResources().getConfiguration();
-
- locale = new Locale(localeString);
- config.locale = locale;
- getResources().updateConfiguration(config, getResources().getDisplayMetrics());
-
-
- return true;
+ prefEdit.putString(context.getString(R.string.key_default_locale_pref), language);
+ prefEdit.apply();
}
-
+
+ /**
@Override
public void onTerminate() {
stopImServiceIfInactive();
@@ -340,47 +420,46 @@ public void onTerminate() {
}
}
+ Imps.clearPassphrase(this);
super.onTerminate();
- }
+ }*/
public synchronized void startImServiceIfNeed() {
startImServiceIfNeed(false);
}
- public synchronized void startImServiceIfNeed(boolean auto) {
+ public synchronized void startImServiceIfNeed(boolean isBoot) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG))
log("start ImService");
Intent serviceIntent = new Intent();
serviceIntent.setComponent(ImServiceConstants.IM_SERVICE_COMPONENT);
- serviceIntent.putExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN, auto);
-
+ serviceIntent.putExtra(ImServiceConstants.EXTRA_CHECK_AUTO_LOGIN, true);
+
if (mImService == null)
{
mApplicationContext.startService(serviceIntent);
-
- mConnectionListener = new MyConnListener(new Handler());
+ if (!isBoot) {
+ mConnectionListener = new MyConnListener(new Handler());
+ }
}
-
- mApplicationContext
- .bindService(serviceIntent, mImServiceConn, Context.BIND_AUTO_CREATE);
-
+ if (mImServiceConn != null && !isBoot)
+ mApplicationContext
+ .bindService(serviceIntent, mImServiceConn, Context.BIND_AUTO_CREATE);
+
+
}
public boolean hasActiveConnections ()
{
return !mConnections.isEmpty();
-
+
}
-
+
public synchronized void stopImServiceIfInactive() {
- boolean hasActiveConnection = true;
- synchronized (mConnections) {
- hasActiveConnection = !mConnections.isEmpty();
- }
- if (!hasActiveConnection) {
+ if (!hasActiveConnections()) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG))
log("stop ImService because there's no active connections");
@@ -391,14 +470,12 @@ public synchronized void stopImServiceIfInactive() {
Intent intent = new Intent();
intent.setComponent(ImServiceConstants.IM_SERVICE_COMPONENT);
mApplicationContext.stopService(intent);
-
+
}
}
-
-
- //private boolean mKillServerOnStart = false;
-
- public synchronized void forceStopImService()
+
+
+ public synchronized void forceStopImService()
{
if (mImService != null) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG))
@@ -410,30 +487,7 @@ public synchronized void forceStopImService()
Intent intent = new Intent();
intent.setComponent(ImServiceConstants.IM_SERVICE_COMPONENT);
mApplicationContext.stopService(intent);
-
- }
- }
-
-
- private CacheWordActivityHandler mCacheWord;
- public void setCacheWord ( CacheWordActivityHandler cacheWord)
- {
- mCacheWord = cacheWord;
- }
-
- public void initOtrStoreKey ()
- {
- if ( getRemoteImService() != null)
- {
- String pkey = SQLCipherOpenHelper.encodeRawKey(mCacheWord.getEncryptionKey());
-
- try {
- getRemoteImService().unlockOtrStore(pkey);
- } catch (RemoteException e) {
-
- LogCleaner.error(ImApp.LOG_TAG, "eror initializing otr key", e);
- }
}
}
@@ -444,9 +498,6 @@ public void onServiceConnected(ComponentName className, IBinder service) {
mImService = IRemoteImService.Stub.asInterface(service);
fetchActiveConnections();
-
- if (mCacheWord != null && mCacheWord.getEncryptionKey() != null)
- initOtrStoreKey();
synchronized (mQueue) {
for (Message msg : mQueue) {
@@ -456,7 +507,7 @@ public void onServiceConnected(ComponentName className, IBinder service) {
}
Message msg = Message.obtain(null, EVENT_SERVICE_CONNECTED);
mBroadcaster.broadcast(msg);
-
+
/*
if (mKillServerOnStart)
{
@@ -468,7 +519,7 @@ public void onServiceDisconnected(ComponentName className) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG))
log("service disconnected");
- //mConnections.clear();
+ mConnections.clear();
mImService = null;
}
};
@@ -478,16 +529,16 @@ public boolean serviceConnected() {
}
// public boolean isBackgroundDataEnabled() { //"background data" is a deprectaed concept
- public boolean isNetworkAvailableAndConnected () {
- ConnectivityManager manager = (ConnectivityManager) mApplicationContext
+ public static boolean isNetworkAvailableAndConnected (Context context) {
+ ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(CONNECTIVITY_SERVICE);
-
+
NetworkInfo nInfo = manager.getActiveNetworkInfo();
if (nInfo != null)
{
- Log.d(LOG_TAG,"network state: available=" + nInfo.isAvailable() + " connected/connecting=" + nInfo.isConnectedOrConnecting());
- return nInfo.isAvailable() && nInfo.isConnectedOrConnecting();
+ Log.d(LOG_TAG,"isNetworkAvailableAndConnected? available=" + nInfo.isAvailable() + " connected=" + nInfo.isConnected());
+ return nInfo.isAvailable() && nInfo.isConnected();
}
else
return false; //no network info is a bad idea
@@ -529,9 +580,10 @@ public static long insertOrUpdateAccount(ContentResolver cr, long providerId, St
}
/** Used to reset the provider settings if a reload is required. */
+ /*
public void resetProviderSettings() {
mProviders = null;
- }
+ }*/
// For testing
public void setImProviderSettings(HashMap providers) {
@@ -539,9 +591,6 @@ public void setImProviderSettings(HashMap providers) {
}
private void loadImProviderSettings() {
- if (mProviders != null) {
- return;
- }
mProviders = new HashMap();
ContentResolver cr = getContentResolver();
@@ -563,6 +612,8 @@ private void loadImProviderSettings() {
String fullName = c.getString(2);
String signUpUrl = c.getString(3);
+ if (mProviders == null) // mProviders has been reset
+ break;
mProviders.put(id, new ProviderDef(id, providerName, fullName, signUpUrl));
}
} finally {
@@ -573,7 +624,7 @@ private void loadImProviderSettings() {
private void loadDefaultBrandingRes() {
HashMap resMapping = new HashMap();
- resMapping.put(BrandingResourceIDs.DRAWABLE_LOGO, R.drawable.ic_launcher_gibberbot);
+ resMapping.put(BrandingResourceIDs.DRAWABLE_LOGO, R.drawable.ic_launcher);
resMapping.put(BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE,
android.R.drawable.presence_online);
resMapping
@@ -646,7 +697,7 @@ private void loadThirdPartyResources() {
Map resMap = plugin.getResourceMap();
//int[] smileyIcons = plugin.getSmileyIconIds();
-
+
BrandingResources res = new BrandingResources(packageRes, resMap,
mDefaultBrandingResources);
mBrandingResources.put(pluginInfo.mProviderName, res);
@@ -692,10 +743,12 @@ public BrandingResources getBrandingResource(long providerId) {
}
public IImConnection createConnection(long providerId, long accountId) throws RemoteException {
+
if (mImService == null) {
// Service hasn't been connected or has died.
return null;
}
+
IImConnection conn = getConnection(providerId);
if (conn == null) {
conn = mImService.createConnection(providerId, accountId);
@@ -705,12 +758,25 @@ public IImConnection createConnection(long providerId, long accountId) throws Re
public IImConnection getConnection(long providerId) {
synchronized (mConnections) {
-
- if (mConnections.size() == 0)
- fetchActiveConnections();
-
-
- return mConnections.get(providerId);
+
+ IImConnection im = mConnections.get(providerId);
+
+ if (im != null)
+ {
+ try
+ {
+ im.getState();
+ }
+ catch (RemoteException doe)
+ {
+ mConnections.clear();
+ //something is wrong
+ fetchActiveConnections();
+ im = mConnections.get(providerId);
+ }
+ }
+
+ return im;
}
}
@@ -729,12 +795,9 @@ public IImConnection getConnectionByAccount(long accountId) {
}
}
- public List getActiveConnections() {
- synchronized (mConnections) {
- ArrayList result = new ArrayList();
- result.addAll(mConnections.values());
- return result;
- }
+ public Collection getActiveConnections() {
+
+ return mConnections.values();
}
public void callWhenServiceConnected(Handler target, Runnable callback) {
@@ -752,22 +815,22 @@ public void callWhenServiceConnected(Handler target, Runnable callback) {
public void deleteAccount (long accountId, long providerId)
{
ContentResolver resolver = getContentResolver();
-
+
Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
resolver.delete(accountUri, null, null);
-
+
Uri providerUri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId);
resolver.delete(providerUri, null, null);
-
+
Uri.Builder builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
ContentUris.appendId(builder, providerId);
- ContentUris.appendId(builder, accountId);
+ ContentUris.appendId(builder, accountId);
resolver.delete(builder.build(), null, null);
-
-
-
+
+
+
}
-
+
public void removePendingCall(Handler target) {
synchronized (mQueue) {
Iterator iter = mQueue.iterator();
@@ -850,14 +913,14 @@ private void fetchActiveConnections() {
// register the listener before fetch so that we won't miss any connection.
mImService.addConnectionCreatedListener(mConnCreationListener);
synchronized (mConnections) {
-
+
for (IBinder binder : (List) mImService.getActiveConnections()) {
IImConnection conn = IImConnection.Stub.asInterface(binder);
long providerId = conn.getProviderId();
- if (!mConnections.containsKey(providerId)) {
+ // if (!mConnections.containsKey(providerId)) {
mConnections.put(providerId, conn);
conn.registerConnectionListener(mConnectionListener);
- }
+ // }
}
}
} catch (RemoteException e) {
@@ -870,10 +933,10 @@ private void fetchActiveConnections() {
public void onConnectionCreated(IImConnection conn) throws RemoteException {
long providerId = conn.getProviderId();
synchronized (mConnections) {
- if (!mConnections.containsKey(providerId)) {
+ // if (!mConnections.containsKey(providerId)) {
mConnections.put(providerId, conn);
conn.registerConnectionListener(mConnectionListener);
- }
+ // }
}
broadcastConnEvent(EVENT_CONNECTION_CREATED, providerId, null);
}
@@ -891,6 +954,9 @@ public void onConnectionStateChange(IImConnection conn, int state, ImErrorInfo e
}
try {
+
+ // fetchActiveConnections();
+
int what = -1;
long providerId = conn.getProviderId();
switch (state) {
@@ -903,20 +969,18 @@ public void onConnectionStateChange(IImConnection conn, int state, ImErrorInfo e
break;
case ImConnection.LOGGING_OUT:
+ // NOTE: if this logic is changed, the logic in ImConnectionAdapter.ConnectionAdapterListener must be changed to match
what = EVENT_CONNECTION_LOGGING_OUT;
- // MIRON - remove only if disconnected!
- // synchronized (mConnections) {
- // mConnections.remove(providerId);
- // }
+
break;
case ImConnection.DISCONNECTED:
+ // NOTE: if this logic is changed, the logic in ImConnectionAdapter.ConnectionAdapterListener must be changed to match
what = EVENT_CONNECTION_DISCONNECTED;
- synchronized (mConnections) {
- mConnections.remove(providerId);
- }
+ // mConnections.remove(providerId);
// stop the service if there isn't an active connection anymore.
stopImServiceIfInactive();
+
break;
case ImConnection.SUSPENDED:
@@ -962,7 +1026,7 @@ public IRemoteImService getRemoteImService() {
return mImService;
}
-
+
public IChatSession getChatSession(long providerId, String remoteAddress) {
IImConnection conn = getConnection(providerId);
@@ -986,5 +1050,31 @@ public IChatSession getChatSession(long providerId, String remoteAddress) {
return null;
}
-
+ public void maybeInit(Activity activity) {
+ startImServiceIfNeed();
+ setAppTheme(activity,null);
+ ImPluginHelper.getInstance(this).loadAvailablePlugins();
+ }
+
+ public void checkForCrashes(final Activity activity) {
+ CrashManager.register(activity, ImApp.HOCKEY_APP_ID, new CrashManagerListener() {
+ @Override
+ public String getDescription() {
+ return Debug.getTrail(activity);
+ }
+ });
+ }
+
+
+ private void initTrustManager ()
+ {
+ PinningTrustManager trustPinning = new PinningTrustManager(SystemKeyStore.getInstance(this),XMPPCertPins.getPinList(), 0);
+ mTrustManager = new MemorizingTrustManager(this, trustPinning);
+
+ }
+
+ public MemorizingTrustManager getTrustManager ()
+ {
+ return mTrustManager;
+ }
}
diff --git a/src/info/guardianproject/otr/app/im/app/ImPluginHelper.java b/src/info/guardianproject/otr/app/im/app/ImPluginHelper.java
index eb13d08b7..09aaf6bd1 100644
--- a/src/info/guardianproject/otr/app/im/app/ImPluginHelper.java
+++ b/src/info/guardianproject/otr/app/im/app/ImPluginHelper.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2009 Myriad Group AG. Copyright (C) 2009 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -104,13 +104,23 @@ public List getProviderNames() {
return names;
}
+ public List getProviderFullNames() {
+ List plugins = getPlugins();
+ List names = new ArrayList();
+ for (ResolveInfo plugin : plugins) {
+ names.add(plugin.serviceInfo.metaData
+ .getString(ImPluginConstants.METADATA_PROVIDER_FULL_NAME));
+ }
+ return names;
+ }
+
public long createAdditionalProvider(String name) {
List plugins = getPlugins();
ResolveInfo info = null;
ServiceInfo serviceInfo = null;
Bundle metaData = null;
long providerId = -1;
-
+
for (ResolveInfo _info : plugins) {
serviceInfo = _info.serviceInfo;
if (serviceInfo == null) {
@@ -146,7 +156,7 @@ public long createAdditionalProvider(String name) {
Log.e(TAG, "Ignore plugin in package: " + serviceInfo.packageName);
return -1;
}
-
+
ImPluginInfo pluginInfo = new ImPluginInfo(providerName, serviceInfo.packageName,
serviceInfo.name, serviceInfo.applicationInfo.sourceDir);
@@ -281,7 +291,7 @@ private long updateProviderDb(ImPlugin plugin, ImPluginInfo info, String provide
if (c == null)
return 0;
-
+
boolean pluginChanged;
try {
if (c.moveToFirst()) {
diff --git a/src/info/guardianproject/otr/app/im/app/ImRingtonePreference.java b/src/info/guardianproject/otr/app/im/app/ImRingtonePreference.java
index bc4643c7b..1e91868f6 100644
--- a/src/info/guardianproject/otr/app/im/app/ImRingtonePreference.java
+++ b/src/info/guardianproject/otr/app/im/app/ImRingtonePreference.java
@@ -1,13 +1,13 @@
/**
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -21,8 +21,10 @@
import info.guardianproject.otr.app.im.service.ImServiceConstants;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.preference.RingtonePreference;
import android.text.TextUtils;
@@ -45,8 +47,13 @@ public ImRingtonePreference(Context context, AttributeSet attrs) {
@Override
protected Uri onRestoreRingtone() {
+
+ ContentResolver cr = getContext().getContentResolver();
+
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
- getContext().getContentResolver(), false /* keep updated */, null /* no handler */);
+ pCursor,cr, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, false /* keep updated */, null /* no handler */);
String uri = settings.getRingtoneURI();
if (Log.isLoggable(ImApp.LOG_TAG, Log.VERBOSE)) {
@@ -66,8 +73,13 @@ protected Uri onRestoreRingtone() {
@Override
protected void onSaveRingtone(Uri ringtoneUri) {
- final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
- getContext().getContentResolver(), false /* keep updated */, null /* no handler */);
+
+ ContentResolver cr = getContext().getContentResolver();
+
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
+ pCursor, cr, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, false /* keep updated */, null /* no handler */);
// When ringtoneUri is null, that means 'Silent' was chosen
settings.setRingtoneURI(ringtoneUri == null ? "" : ringtoneUri.toString());
diff --git a/src/info/guardianproject/otr/app/im/app/ImUrlActivity.java b/src/info/guardianproject/otr/app/im/app/ImUrlActivity.java
index 4b4ff3dd6..fdfbeadc9 100644
--- a/src/info/guardianproject/otr/app/im/app/ImUrlActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/ImUrlActivity.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -16,9 +16,7 @@
*/
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.cacheword.CacheWordActivityHandler;
-import info.guardianproject.cacheword.ICacheWordSubscriber;
-import info.guardianproject.cacheword.SQLCipherOpenHelper;
+import info.guardianproject.otr.OtrDataHandler;
import info.guardianproject.otr.app.im.IChatSession;
import info.guardianproject.otr.app.im.IChatSessionManager;
import info.guardianproject.otr.app.im.IImConnection;
@@ -27,11 +25,19 @@
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
import info.guardianproject.util.LogCleaner;
+import info.guardianproject.util.SystemServices;
+import info.guardianproject.util.SystemServices.FileInfo;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
+import java.util.Locale;
import java.util.Set;
+import java.util.UUID;
+import net.java.otr4j.session.SessionStatus;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -41,121 +47,241 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.TextView;
+import android.widget.Toast;
-public class ImUrlActivity extends ThemeableActivity implements ICacheWordSubscriber {
- private static final String[] ACCOUNT_PROJECTION = { Imps.Account._ID, Imps.Account.PASSWORD, };
- private static final int ACCOUNT_ID_COLUMN = 0;
- private static final int ACCOUNT_PW_COLUMN = 1;
+public class ImUrlActivity extends Activity {
+ private static final String TAG = "ImUrlActivity";
+
+ private static final int REQUEST_PICK_CONTACTS = RESULT_FIRST_USER + 1;
+ private static final int REQUEST_CREATE_ACCOUNT = RESULT_FIRST_USER + 2;
+ private static final int REQUEST_SIGNIN_ACCOUNT = RESULT_FIRST_USER + 3;
+ private static final int REQUEST_START_MUC = RESULT_FIRST_USER + 4;
private String mProviderName;
private String mToAddress;
+ private String mFromAddress;
+ private String mHost;
- private ImApp mApp;
private IImConnection mConn;
+ private IChatSessionManager mChatSessionManager;
+
+ private Uri mSendUri;
+ private String mSendType;
+ private String mSendText;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- CacheWordActivityHandler cacheWord = new CacheWordActivityHandler(this, (ICacheWordSubscriber)this);
- cacheWord.connectToService();
-
-
- Intent intent = getIntent();
- if (Intent.ACTION_SENDTO.equals(intent.getAction())) {
- if (!resolveIntent(intent)) {
- finish();
- return;
- }
+ doOnCreate();
+ }
- if (TextUtils.isEmpty(mToAddress)) {
- LogCleaner.warn(ImApp.LOG_TAG, "Invalid to address");
- // finish();
- return;
- }
- mApp = (ImApp)getApplication();
-
- mApp.callWhenServiceConnected(new Handler(), new Runnable() {
- public void run() {
- handleIntent();
- }
- });
- } else {
- finish();
- }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ setIntent(intent);
+ }
+
+ public void onDBLocked() {
+
+ Intent intent = new Intent(getApplicationContext(), WelcomeActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ finish();
}
void handleIntent() {
+
ContentResolver cr = getContentResolver();
-
-
- long providerId = -1;// = Imps.Provider.getProviderIdForName(cr, mProviderName);
- long accountId;
-
- List listConns = ((ImApp)getApplication()).getActiveConnections();
-
- if (!listConns.isEmpty())
+
+ long providerId = -1;
+ long accountId = -1;
+
+ Collection listConns = ((ImApp)getApplication()).getActiveConnections();
+
+ //look for active connections that match the host we need
+ for (IImConnection conn : listConns)
{
-
- mConn = listConns.get(0);
+
+
try {
- providerId = mConn.getProviderId();
- accountId = mConn.getAccountId();
+ long connProviderId = conn.getProviderId();
+
+ Cursor cursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(connProviderId)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
+ cursor, cr, connProviderId, false /* don't keep updated */, null /* no handler */);
+
+ try {
+ String domainToCheck = settings.getDomain();
+
+ if (domainToCheck != null && domainToCheck.length() > 0 && mHost.contains(domainToCheck))
+ {
+ mConn = conn;
+ providerId = connProviderId;
+ accountId = conn.getAccountId();
+
+ break;
+ }
+ } finally {
+ settings.close();
+ }
+
} catch (RemoteException e) {
e.printStackTrace();
}
- }
-// mConn = mApp.getConnection(providerId);
+ }
+
+ //nothing active, let's see if non-active connections match
if (mConn == null) {
- Cursor c = DatabaseUtils.queryAccountsForProvider(cr, ACCOUNT_PROJECTION, providerId);
- if (c == null) {
- addAccount(providerId);
+
+ Cursor cursorProvider = initProviderCursor();
+
+ if (cursorProvider == null || cursorProvider.isClosed() || cursorProvider.getCount() == 0) {
+
+ createNewAccount();
+ return;
} else {
- accountId = c.getLong(ACCOUNT_ID_COLUMN);
- if (c.isNull(ACCOUNT_PW_COLUMN)) {
- editAccount(accountId);
- } else {
- signInAccount(accountId);
+
+
+ while (cursorProvider.moveToNext())
+ {
+ //make sure there is a stored password
+ if (!cursorProvider.isNull(ACTIVE_ACCOUNT_PW_COLUMN)) {
+
+ long cProviderId = cursorProvider.getLong(PROVIDER_ID_COLUMN);
+ Cursor cursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(cProviderId)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
+ cursor, cr, cProviderId, false /* don't keep updated */, null /* no handler */);
+
+ //does the conference host we need, match the settings domain for a logged in account
+ String domainToCheck = settings.getDomain();
+
+ if (domainToCheck != null && domainToCheck.length() > 0 && mHost.contains(domainToCheck))
+ {
+ providerId = cProviderId;
+ accountId = cursorProvider.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+ mConn = ((ImApp)getApplication()).getConnection(providerId);
+
+
+ //now sign in
+ signInAccount(accountId, providerId, cursorProvider.getString(ACTIVE_ACCOUNT_PW_COLUMN));
+
+
+ settings.close();
+ cursorProvider.close();
+
+ return;
+
+ }
+
+ settings.close();
+
+ }
+
}
+
+ cursorProvider.close();
+
+
+
+
+
}
- } else {
+
+ }
+
+ if (mConn != null)
+ {
try {
int state = mConn.getState();
accountId = mConn.getAccountId();
+ providerId = mConn.getProviderId();
if (state < ImConnection.LOGGED_IN) {
- signInAccount(accountId);
- } else if (state == ImConnection.LOGGED_IN || state == ImConnection.SUSPENDED) {
- if (!isValidToAddress()) {
+
+ Cursor cursorProvider = initProviderCursor();
+
+ while(cursorProvider.moveToNext())
+ {
+ if (cursorProvider.getLong(ACTIVE_ACCOUNT_ID_COLUMN) == accountId)
+ {
+ signInAccount(accountId, providerId, cursorProvider.getString(ACTIVE_ACCOUNT_PW_COLUMN));
+
+ try {
+ Thread.sleep (500);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }//wait here for three seconds
+ mConn = ((ImApp)getApplication()).getConnection(providerId);
+
+ break;
+ }
+ }
+
+ cursorProvider.close();
+ }
+
+ if (state == ImConnection.LOGGED_IN || state == ImConnection.SUSPENDED) {
+
+ Uri data = getIntent().getData();
+
+ if (data.getScheme().equals("immu"))
+ {
+ this.openMultiUserChat(data);
+
+ }
+ else if (!isValidToAddress()) {
showContactList(accountId);
} else {
openChat(providerId, accountId);
}
+
+
+
}
} catch (RemoteException e) {
// Ouch! Service died! We'll just disappear.
Log.w("ImUrlActivity", "Connection disappeared!");
+ finish();
}
}
- finish();
+ else
+ {
+ createNewAccount();
+ return;
+ }
}
+ /*
private void addAccount(long providerId) {
Intent intent = new Intent(this, AccountActivity.class);
intent.setAction(Intent.ACTION_INSERT);
intent.setData(ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId));
- intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
+// intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
+
+ if (mFromAddress != null)
+ intent.putExtra("newuser", mFromAddress + '@' + mHost);
startActivity(intent);
- }
+ }*/
private void editAccount(long accountId) {
Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
@@ -163,12 +289,33 @@ private void editAccount(long accountId) {
intent.setAction(Intent.ACTION_EDIT);
intent.setData(accountUri);
intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
- startActivity(intent);
+ startActivityForResult(intent,REQUEST_SIGNIN_ACCOUNT);
}
- private void signInAccount(long accountId) {
- editAccount(accountId);
+ private void signInAccount(long accountId, long providerId, String password) {
+ //editAccount(accountId);
// TODO sign in? security implications?
+ SignInHelper signInHelper = new SignInHelper(this);
+ signInHelper.setSignInListener(new SignInHelper.SignInListener() {
+ public void connectedToService() {
+ }
+ public void stateChanged(int state, long accountId) {
+ if (state == ImConnection.LOGGED_IN) {
+
+ mHandlerRouter.post(new Runnable()
+ {
+ public void run ()
+ {
+ handleIntent();
+ }
+ });
+
+ }
+
+ }
+ });
+
+ signInHelper.signIn(password, providerId, accountId, true);
}
private void showContactList(long accountId) {
@@ -185,7 +332,7 @@ private void openChat(long provider, long account) {
IChatSessionManager manager = mConn.getChatSessionManager();
IChatSession session = manager.getChatSession(mToAddress);
if (session == null) {
- session = manager.createChatSession(mToAddress);
+ session = manager.createChatSession(mToAddress,false);
}
Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, session.getId());
@@ -201,22 +348,65 @@ private void openChat(long provider, long account) {
}
}
- private boolean resolveIntent(Intent intent) {
+ private boolean resolveInsertIntent(Intent intent) {
Uri data = intent.getData();
- String host = data.getHost();
-
- if (data.getScheme().equals("immu"))
+
+ if (data.getScheme().equals("ima"))
{
- this.openMultiUserChat(data);
-
+ createNewAccount();
+
+ return true;
+ }
+ return false;
+ }
+
+ // private static final String USERNAME_PATTERN = "^[a-z0-9_-]{3,15}$";
+
+ //private static final String USERNAME_NON_LETTERS_UNICODE = "[^\\p{L}\\p{Nd}]+";
+ //private static final String USERNAME_NON_LETTERS_ALPHANUM = "[\\d[^\\w]]+";
+ private static final String USERNAME_ONLY_ALPHANUM = "[^A-Za-z0-9]";
+
+ private boolean resolveIntent(Intent intent) {
+ Uri data = intent.getData();
+ mHost = data.getHost();
+
+ if (data.getScheme().equals("immu")) {
+ mFromAddress = data.getUserInfo();
+
+ //remove username non-letters
+ mFromAddress = mFromAddress.replaceAll(USERNAME_ONLY_ALPHANUM, "").toLowerCase(Locale.ENGLISH);
+
+ String chatRoom = null;
+
+ if (data.getPathSegments().size() > 0)
+ {
+ chatRoom = data.getPathSegments().get(0);
+
+ //replace chat room name non-letters with underscores
+ chatRoom = chatRoom.replaceAll("[\\d[^\\w]]+", "_");
+
+ mToAddress = chatRoom + '@' + mHost;
+
+ mProviderName = findMatchingProvider(mHost);
+
+ return true;
+ }
+
+ return false;
+
+ }
+
+ if (data.getScheme().equals("otr-in-band")) {
+ this.openOtrInBand(data, intent.getType());
+
return true;
- }
+ }
if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)) {
- log("resolveIntent: host=" + host);
+ log("resolveIntent: host=" + mHost);
}
- if (TextUtils.isEmpty(host)) {
+ if (TextUtils.isEmpty(mHost)) {
Set categories = intent.getCategories();
if (categories != null) {
Iterator iter = categories.iterator();
@@ -231,13 +421,13 @@ private boolean resolveIntent(Intent intent) {
}
}
}
-
+
mToAddress = data.getSchemeSpecificPart();
} else {
- mProviderName = findMatchingProvider(host);
+ mProviderName = findMatchingProvider(mHost);
if (mProviderName == null) {
- Log.w(ImApp.LOG_TAG, "resolveIntent: IM provider " + host + " not supported");
+ Log.w(ImApp.LOG_TAG, "resolveIntent: IM provider " + mHost + " not supported");
return false;
}
@@ -270,10 +460,12 @@ private String findMatchingProvider(String provider) {
return null;
}
- if (provider.equalsIgnoreCase("xmpp"))
- return Imps.ProviderNames.XMPP;
-
- return null;
+// if (provider.equalsIgnoreCase("xmpp"))
+ // return Imps.ProviderNames.XMPP;
+
+
+ return "Jabber (XMPP)";
+ //return Imps.ProviderNames.XMPP;
}
private boolean isValidToAddress() {
@@ -291,27 +483,22 @@ private boolean isValidToAddress() {
private static void log(String msg) {
Log.d(ImApp.LOG_TAG, " " + msg);
}
-
- @Override
- public void onCacheWordUninitialized() {
- Log.d(ImApp.LOG_TAG,"cache word uninit");
-
- showLockScreen();
- }
-
+
void openMultiUserChat(final Uri data) {
-
- new AlertDialog.Builder(this)
- .setTitle("Join Chat Room?")
- .setMessage("An external app is attempting to connect you to a chatroom. Allow?")
+
+ new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.dialog_connect_chatroom_title))
+ .setMessage(getString(R.string.dialog_connect_chatroom_message))
.setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- Intent intent = new Intent(ImUrlActivity.this, NewChatActivity.class);
+ Intent intent = new Intent(ImUrlActivity.this, NewChatActivity.class);
intent.setData(data);
- startActivity(intent);
-
+ ImUrlActivity.this.startActivityForResult(intent, REQUEST_START_MUC);
+
+ dialog.dismiss();
+ finish();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@@ -322,27 +509,365 @@ public void onClick(DialogInterface dialog, int whichButton) {
}
})
.create().show();
+
+
+ }
+
+ void createNewAccount() {
+
+ String username = getIntent().getData().getUserInfo();
+ String appCreateAcct = String.format(getString(R.string.allow_s_to_create_a_new_chat_account_for_s_),username);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.prompt_create_new_account_)
+ .setMessage(appCreateAcct)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+
+ mHandlerRouter.sendEmptyMessage(1);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ finish();
+ }
+ })
+ .create().show();
+ }
+
+ Handler mHandlerRouter = new Handler ()
+ {
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ if (msg.what == 1)
+ {
+ Uri uriAccountData = getIntent().getData();
+
+ if (uriAccountData.getScheme().equals("immu"))
+ {
+ //need to generate proper IMA url for account setup
+ String randomJid = ((int)(Math.random()*1000))+"";
+ String regUser = mFromAddress + randomJid;
+ String regPass = UUID.randomUUID().toString().substring(0,16);
+ String regDomain = mHost.replace("conference.", "");
+ uriAccountData = Uri.parse("ima://" + regUser + ':' + regPass + '@' + regDomain);
+ }
+
+ Intent intent = new Intent(ImUrlActivity.this, AccountActivity.class);
+ intent.setAction(Intent.ACTION_INSERT);
+ intent.setData(uriAccountData);
+ startActivityForResult(intent,REQUEST_CREATE_ACCOUNT);
+
+ }
+ else if (msg.what == 2)
+ {
+ doOnCreate();
+ }
+ }
+
+ };
+
+ void openOtrInBand(final Uri data, final String type) {
+
+ mSendType = getContentResolver().getType(data);
-
+ if (mSendType != null ) {
+
+ mSendUri = data;
+ startContactPicker();
+ return;
+ }
+ else if (data.toString().startsWith(OtrDataHandler.URI_PREFIX_OTR_IN_BAND))
+ {
+ String localUrl = data.toString().replaceFirst(OtrDataHandler.URI_PREFIX_OTR_IN_BAND, "");
+ FileInfo info = null;
+ if (TextUtils.equals(data.getAuthority(), "com.android.contacts")) {
+ info = SystemServices.getContactAsVCardFile(this, data);
+ } else {
+ info = SystemServices.getFileInfoFromURI(ImUrlActivity.this, data);
+ }
+ if (info != null && !TextUtils.isEmpty(info.path)) {
+ mSendUri = Uri.fromFile(new File(info.path));
+ mSendType = type != null ? type : info.type;
+ startContactPicker();
+ return;
+ }
+ }
+
+ Toast.makeText(this, R.string.unsupported_incoming_data, Toast.LENGTH_LONG).show();
+ finish(); // make sure not to show this Activity's blank white screen
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) {
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_PICK_CONTACTS) {
+ String username = resultIntent.getExtras().getString(ContactsPickerActivity.EXTRA_RESULT_USERNAME);
+ long providerId = resultIntent.getExtras().getLong(ContactsPickerActivity.EXTRA_RESULT_PROVIDER);
+ long accountId = resultIntent.getExtras().getLong(ContactsPickerActivity.EXTRA_RESULT_ACCOUNT);
+
+ sendOtrInBand(username, providerId, accountId);
+ finish();
+ }
+ else if (requestCode == REQUEST_SIGNIN_ACCOUNT || requestCode == REQUEST_CREATE_ACCOUNT)
+ {
+
+ mHandlerRouter.postDelayed(new Runnable()
+ {
+ @Override
+ public void run ()
+ {
+ doOnCreate();
+ }
+ }, 500);
+
+ }
+
+ } else {
+ finish();
+ }
+ }
+
+ private void sendOtrInBand(String username, long providerId, long accountId) {
+
+ try
+ {
+ IImConnection conn = ((ImApp)getApplication()).getConnection(providerId);
+ mChatSessionManager = conn.getChatSessionManager();
+
+ IChatSession session = getChatSession(username);
+
+ if (mSendText != null)
+ session.sendMessage(mSendText);
+ else if (mSendUri != null && session.getOtrChatSession() != null)
+ {
+
+ if (session.getOtrChatSession().getChatStatus() != SessionStatus.ENCRYPTED.ordinal())
+ {
+ //can't do OTR transfer
+ Toast.makeText(this, R.string.err_otr_share_no_encryption, Toast.LENGTH_LONG).show();
+ }
+ else
+ {
+ try {
+
+
+ String offerId = UUID.randomUUID().toString();
+ // Log.i(TAG, "mSendUrl " +mSendUrl);
+ Uri vfsUri = null;
+
+ if (ChatFileStore.isVfsUri(mSendUri))
+ vfsUri = mSendUri;
+ else
+ {
+ InputStream is = getContentResolver().openInputStream(mSendUri);
+ String fileName = mSendUri.getLastPathSegment();
+ FileInfo importInfo = SystemServices.getFileInfoFromURI(this, mSendUri);
+
+ if (importInfo.type != null && importInfo.type.startsWith("image"))
+ vfsUri = ChatFileStore.resizeAndImportImage(this, session.getId() + "", mSendUri, importInfo.type);
+ else
+ vfsUri = ChatFileStore.importContent(session.getId() + "", fileName, is);
+
+ }
+
+ FileInfo info = SystemServices.getFileInfoFromURI(this, vfsUri);
+ session.offerData(offerId, info.path, mSendType );
+
+ Imps.insertMessageInDb(
+ getContentResolver(), false, session.getId(), true, null, vfsUri.toString(),
+ System.currentTimeMillis(), Imps.MessageType.OUTGOING_ENCRYPTED, // TODO show verified status
+ 0, offerId, mSendType);
+
+
+ } catch (Exception e) {
+
+ Toast.makeText(this, R.string.unable_to_securely_share_this_file, Toast.LENGTH_LONG).show();
+
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ catch (RemoteException e)
+ {
+ e.printStackTrace();
+ }
+
+ }
+
+ private IChatSession getChatSession(String username) {
+ if (mChatSessionManager != null) {
+ try {
+ IChatSession session = mChatSessionManager.getChatSession(username);
+
+ if (session == null)
+ session = mChatSessionManager.createChatSession(username,false);
+
+ return session;
+ } catch (RemoteException e) {
+ LogCleaner.error(ImApp.LOG_TAG, "send message error",e);
+ }
+ }
+ return null;
+ }
+
+ private void startContactPicker() {
+
+ boolean noOnlineConnections = true;
+ Uri.Builder builder = Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
+ Collection listConns = ((ImApp)getApplication()).getActiveConnections();
+
+ for (IImConnection conn : listConns)
+ {
+ try
+ {
+ if (conn.getState() == ImConnection.LOGGED_IN)
+ {
+ try {
+ mChatSessionManager = conn.getChatSessionManager();
+ long mProviderId = conn.getProviderId();
+ long mAccountId = conn.getAccountId();
+
+ ContentUris.appendId(builder, mProviderId);
+ ContentUris.appendId(builder, mAccountId);
+ Uri data = builder.build();
+
+ Intent i = new Intent(Intent.ACTION_PICK, data);
+ startActivityForResult(i, REQUEST_PICK_CONTACTS);
+ noOnlineConnections = false;
+ break;
+
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ catch (RemoteException re){}
+ }
+ if (noOnlineConnections) {
+ Toast.makeText(this, R.string.no_connection_for_sending, Toast.LENGTH_LONG).show();
+ finish(); // quit this Activity, nothing online
+ }
}
void showLockScreen() {
Intent intent = new Intent(this, LockScreenActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ // intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("originalIntent", getIntent());
startActivity(intent);
-
+ finish();
+
}
-
- @Override
- public void onCacheWordLocked() {
-
- showLockScreen();
+ private void doOnCreate ()
+ {
+ Intent intent = getIntent();
+ if (Intent.ACTION_INSERT.equals(intent.getAction())) {
+ if (!resolveInsertIntent(intent)) {
+ finish();
+ return;
+ }
+ } else if (Intent.ACTION_SEND.equals(intent.getAction())) {
+
+ Uri streamUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
+ String mimeType = intent.getType();
+ String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
+
+ if (streamUri != null)
+ openOtrInBand(streamUri, mimeType);
+ else if (intent.getData() != null)
+ openOtrInBand(intent.getData(), mimeType);
+ else if (sharedText != null)
+ {
+ //do nothing for now :(
+ mSendText = sharedText;
+
+ startContactPicker();
+
+ }
+ else
+ finish();
+
+ } else if (Intent.ACTION_SENDTO.equals(intent.getAction())) {
+ if (!resolveIntent(intent)) {
+ finish();
+ return;
+ }
+
+ if (TextUtils.isEmpty(mToAddress)) {
+ LogCleaner.warn(ImApp.LOG_TAG, "Invalid to address");
+ // finish();
+ return;
+ }
+
+ ImApp mApp = (ImApp)getApplication();
+
+ if (mApp.serviceConnected())
+ handleIntent();
+ else
+ {
+ mApp.callWhenServiceConnected(new Handler(), new Runnable() {
+ public void run() {
+
+ handleIntent();
+ }
+ });
+ Toast.makeText(ImUrlActivity.this, R.string.starting_the_chatsecure_service_, Toast.LENGTH_LONG).show();
+
+ }
+ } else {
+ finish();
+ }
+ }
+
+ private Cursor initProviderCursor ()
+ {
+ Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT;
+ // uri = uri.buildUpon().appendQueryParameter(ImApp.CACHEWORD_PASSWORD_KEY, pkey).build();
+
+ //just init the contentprovider db
+ return getContentResolver().query(uri, PROVIDER_PROJECTION,
+ Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL" /* selection */,
+ new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
+ Imps.Provider.DEFAULT_SORT_ORDER);
+
}
+
+
@Override
- public void onCacheWordOpened() {
-
+ protected void onDestroy() {
+ super.onDestroy();
}
+
+ private static final String[] PROVIDER_PROJECTION = {
+ Imps.Provider._ID,
+ Imps.Provider.NAME,
+ Imps.Provider.FULLNAME,
+ Imps.Provider.CATEGORY,
+ Imps.Provider.ACTIVE_ACCOUNT_ID,
+ Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
+ Imps.Provider.ACTIVE_ACCOUNT_PW,
+ Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
+ Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
+ Imps.Provider.ACCOUNT_PRESENCE_STATUS,
+ Imps.Provider.ACCOUNT_CONNECTION_STATUS
+ };
+
+
+ static final int PROVIDER_ID_COLUMN = 0;
+ static final int PROVIDER_NAME_COLUMN = 1;
+ static final int PROVIDER_FULLNAME_COLUMN = 2;
+ static final int PROVIDER_CATEGORY_COLUMN = 3;
+ static final int ACTIVE_ACCOUNT_ID_COLUMN = 4;
+ static final int ACTIVE_ACCOUNT_USERNAME_COLUMN = 5;
+ static final int ACTIVE_ACCOUNT_PW_COLUMN = 6;
+ static final int ACTIVE_ACCOUNT_LOCKED = 7;
+ static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
+ static final int ACCOUNT_PRESENCE_STATUS = 9;
+ static final int ACCOUNT_CONNECTION_STATUS = 10;
}
diff --git a/src/info/guardianproject/otr/app/im/app/ImageListAdapter.java b/src/info/guardianproject/otr/app/im/app/ImageListAdapter.java
index 8659b3711..9ab9591df 100644
--- a/src/info/guardianproject/otr/app/im/app/ImageListAdapter.java
+++ b/src/info/guardianproject/otr/app/im/app/ImageListAdapter.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/IntTrie.java b/src/info/guardianproject/otr/app/im/app/IntTrie.java
index c8ee83166..6292fe277 100644
--- a/src/info/guardianproject/otr/app/im/app/IntTrie.java
+++ b/src/info/guardianproject/otr/app/im/app/IntTrie.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/LockScreenActivity.java b/src/info/guardianproject/otr/app/im/app/LockScreenActivity.java
index 3553c878b..bd378afbe 100644
--- a/src/info/guardianproject/otr/app/im/app/LockScreenActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/LockScreenActivity.java
@@ -1,12 +1,9 @@
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.cacheword.CacheWordActivityHandler;
-import info.guardianproject.cacheword.ICacheWordSubscriber;
-import info.guardianproject.otr.app.im.R;
-
-import java.security.GeneralSecurityException;
-
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -19,17 +16,26 @@
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import android.widget.ViewFlipper;
-import com.actionbarsherlock.app.SherlockActivity;
+import info.guardianproject.cacheword.CacheWordActivityHandler;
+import info.guardianproject.cacheword.ICacheWordSubscriber;
+import info.guardianproject.otr.app.im.R;
+import info.guardianproject.otr.app.im.provider.Imps;
+import info.guardianproject.util.BackgroundBitmapLoaderTask;
+import info.guardianproject.util.Languages;
+
+import java.security.GeneralSecurityException;
-public class LockScreenActivity extends SherlockActivity implements ICacheWordSubscriber {
+public class LockScreenActivity extends ThemeableActivity implements ICacheWordSubscriber {
private static final String TAG = "LockScreenActivity";
private final static int MIN_PASS_LENGTH = 4;
@@ -41,26 +47,35 @@ public class LockScreenActivity extends SherlockActivity implements ICacheWordSu
private EditText mConfirmNewPassphrase;
private View mViewCreatePassphrase;
private View mViewEnterPassphrase;
- private Button mBtnOpen;
+ private ImageButton mLanguageButton;
+
private CacheWordActivityHandler mCacheWord;
private String mPasswordError;
private TwoViewSlider mSlider;
+ private ImApp mApp;
+ private Button mBtnCreate;
+ private Button mBtnSkip;
+
+
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
-
- getSherlock().getActionBar().hide();
-
+
+ mApp = (ImApp)getApplication();
+
+ getSupportActionBar().hide();
+
setContentView(R.layout.activity_lock_screen);
-
- mCacheWord = new CacheWordActivityHandler(this, (ICacheWordSubscriber)this);
-
+
+ mCacheWord = new CacheWordActivityHandler(mApp, (ICacheWordSubscriber)this);
+
mViewCreatePassphrase = findViewById(R.id.llCreatePassphrase);
mViewEnterPassphrase = findViewById(R.id.llEnterPassphrase);
mEnterPassphrase = (EditText) findViewById(R.id.editEnterPassphrase);
+
mNewPassphrase = (EditText) findViewById(R.id.editNewPassphrase);
mConfirmNewPassphrase = (EditText) findViewById(R.id.editConfirmNewPassphrase);
ViewFlipper vf = (ViewFlipper) findViewById(R.id.viewFlipper1);
@@ -69,29 +84,56 @@ protected void onCreate(Bundle savedInstanceState)
mSlider = new TwoViewSlider(vf, flipView1, flipView2, mNewPassphrase, mConfirmNewPassphrase);
+ // set up language chooser button
+ mLanguageButton = (ImageButton) findViewById(R.id.languageButton);
+ mLanguageButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Activity activity = LockScreenActivity.this;
+ final Languages languages = Languages.get(activity);
+ final ArrayAdapter languagesAdapter = new ArrayAdapter(activity,
+ android.R.layout.simple_list_item_single_choice, languages.getAllNames());
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setIcon(R.drawable.ic_settings_language);
+ builder.setTitle(R.string.KEY_PREF_LANGUAGE_TITLE);
+ builder.setAdapter(languagesAdapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int position) {
+ Log.i(TAG, "onItemClick: " + dialog + " " + position);
+ String[] languageCodes = languages.getSupportedLocales();
+ resetLanguage(languageCodes[position]);
+ dialog.dismiss();
+ }
+ });
+ builder.show();
+ }
+ });
+
}
@Override
protected void onPause() {
super.onPause();
- try { mCacheWord.onPause();}
- catch (Exception e){}
+ mCacheWord.onPause();
+
}
@Override
protected void onResume() {
super.onResume();
-
- try
- {
- mCacheWord.onResume();
- }
- catch (Exception e){}
+ mCacheWord.onResume();
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mCacheWord.disconnect();
}
@Override
public void onBackPressed() {
-
+
//do nothing!
}
@@ -121,10 +163,47 @@ private boolean isPasswordValid() {
return validatePassword(mNewPassphrase.getText().toString().toCharArray());
}
+ private boolean isPasswordFieldEmpty() {
+ return mNewPassphrase.getText().toString().length() == 0;
+ }
+
private boolean isConfirmationFieldEmpty() {
return mConfirmNewPassphrase.getText().toString().length() == 0;
}
+ private void initializeWithPassphrase() {
+ try {
+ String passphrase = mNewPassphrase.getText().toString();
+ if (passphrase.isEmpty()) {
+ // Create DB with empty passphrase
+ if (Imps.setEmptyPassphrase(this, false)) {
+
+ try
+ {
+ ChatFileStore.initWithoutPassword(this);
+
+ // Simulate cacheword opening
+ afterCacheWordOpened();
+ }
+ catch (Exception e)
+ {
+ Log.d(ImApp.LOG_TAG,"unable to mount VFS store"); //but let's not crash the whole app right now
+ }
+
+ ChatFileStore.initWithoutPassword(this);
+
+ } else {
+ // TODO failed
+ }
+ } else {
+ mCacheWord.setPassphrase(passphrase.toCharArray());
+ }
+ } catch (Exception e) {
+ // TODO initialization failed
+ Log.e(TAG, "Cacheword pass initialization failed: " + e.getMessage());
+ }
+ }
+
private void initializePassphrase() {
// Passphrase is not set, so allow the user to create one
@@ -141,7 +220,9 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
if (!isPasswordValid())
showValidationError();
- else
+ else if (isPasswordFieldEmpty()) {
+ initializeWithPassphrase();
+ } else
mSlider.showConfirmationField();
}
return false;
@@ -164,57 +245,53 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
}
});
- Button btnCreate = (Button) findViewById(R.id.btnCreate);
- btnCreate.setOnClickListener(new OnClickListener()
+ mBtnCreate = (Button) findViewById(R.id.btnCreate);
+ mBtnCreate.setOnClickListener(new OnClickListener()
{
+ @Override
public void onClick(View v)
{
// validate
if (!isPasswordValid()) {
showValidationError();
mSlider.showNewPasswordField();
- } else if (isConfirmationFieldEmpty()) {
+ } else if (isConfirmationFieldEmpty() && !isPasswordFieldEmpty()) {
+ mBtnSkip.setVisibility(View.GONE);
mSlider.showConfirmationField();
- } else if (!newEqualsConfirmation()) {
+ mBtnCreate.setText(R.string.lock_screen_confirm_passphrase);
+ }
+ else if (!newEqualsConfirmation()) {
showInequalityError();
- mSlider.showNewPasswordField();
- } else {
- try {
- mCacheWord.setPassphrase(mNewPassphrase.getText().toString().toCharArray());
- } catch (GeneralSecurityException e) {
- // TODO initialization failed
- Log.e(TAG, "Cacheword pass initialization failed: " + e.getMessage());
- }
+ }
+ else if (!isConfirmationFieldEmpty() && !isPasswordFieldEmpty())
+ {
+ initializeWithPassphrase();
}
}
});
- }
- private void promptPassphrase() {
- mViewCreatePassphrase.setVisibility(View.GONE);
- mViewEnterPassphrase.setVisibility(View.VISIBLE);
- mBtnOpen = (Button) findViewById(R.id.btnOpen);
- mBtnOpen.setOnClickListener(new OnClickListener()
- {
+
+ mBtnSkip = (Button)findViewById(R.id.btnSkip);
+ mBtnSkip.setOnClickListener(new OnClickListener(){
+
+ @Override
public void onClick(View v)
{
- if (mEnterPassphrase.getText().toString().length() == 0)
- return;
- // Check passphrase
- try {
- mCacheWord.setPassphrase(mEnterPassphrase.getText().toString().toCharArray());
- } catch (GeneralSecurityException e) {
- mEnterPassphrase.setText("");
- // TODO implement try again and wipe if fail
- Log.e(TAG, "Cacheword pass verification failed: " + e.getMessage());
- return;
- }
+ if (isPasswordFieldEmpty())
+ initializeWithPassphrase();
+
}
});
+ }
+
+ private void promptPassphrase() {
+ mViewCreatePassphrase.setVisibility(View.GONE);
+ mViewEnterPassphrase.setVisibility(View.VISIBLE);
mEnterPassphrase.setOnEditorActionListener(new OnEditorActionListener()
{
+ @Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_GO)
@@ -228,7 +305,22 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
protected void onReceiveResult(int resultCode, Bundle resultData)
{
super.onReceiveResult(resultCode, resultData);
- mBtnOpen.performClick();
+
+ if (mEnterPassphrase.getText().toString().length() == 0)
+ return;
+ // Check passphrase
+ try {
+ char[] passphrase = mEnterPassphrase.getText().toString().toCharArray();
+
+ mCacheWord.setPassphrase(passphrase);
+ ImApp.mUsingCacheword = true;
+ } catch (Exception e) {
+ mEnterPassphrase.setText("");
+ // TODO implement try again and wipe if fail
+ Log.e(TAG, "Cacheword pass verification failed: " + e.getMessage());
+ return;
+ }
+
}
});
return true;
@@ -240,44 +332,14 @@ protected void onReceiveResult(int resultCode, Bundle resultData)
private boolean validatePassword(char[] pass)
{
- boolean upper = false;
- boolean lower = false;
- boolean number = false;
- for (char c : pass) {
- if (Character.isUpperCase(c)) {
- upper = true;
- } else if (Character.isLowerCase(c)) {
- lower = true;
- } else if (Character.isDigit(c)) {
- number = true;
- }
- }
- if (pass.length < MIN_PASS_LENGTH)
+ if (pass.length < MIN_PASS_LENGTH && pass.length != 0)
{
// should we support some user string message here?
mPasswordError = getString(R.string.pass_err_length);
return false;
}
- //only enforce password length
- /*
- else if (!upper)
- {
- mPasswordError = getString(R.string.pass_err_upper);
- return false;
- }
- else if (!lower)
- {
- mPasswordError = getString(R.string.pass_err_lower);
- return false;
- }
- else if (!number)
- {
- mPasswordError = getString(R.string.pass_err_num);
- return false;
- }*/
- // if it got here, then must be okay
- // hopefully the user can remember it
+
return true;
}
@@ -343,29 +405,64 @@ private void flip() {
@Override
public void onCacheWordUninitialized() {
+
+ Intent intentOrig;
+
+ if ((intentOrig = getIntent().getParcelableExtra("originalIntent"))!=null)
+ {
+ if (intentOrig.getData() != null)
+ {
+ if (intentOrig.getData().getScheme().equals("immu")||
+ intentOrig.getData().getScheme().equals("ima"))
+ {
+
+ initializeWithPassphrase();
+ return;
+ }
+ }
+ }
+
+
initializePassphrase();
-
+
}
@Override
public void onCacheWordLocked() {
promptPassphrase();
-
}
@Override
public void onCacheWordOpened() {
+ afterCacheWordOpened();
+ ChatFileStore.init(this, mCacheWord.getEncryptionKey());
+ }
+
+ /**
+ *
+ */
+ private void afterCacheWordOpened() {
Intent intent = (Intent) getIntent().getParcelableExtra("originalIntent");
-
+
if (intent != null)
{
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- startActivity(intent);
+ getIntent().removeExtra("originalIntent");
finish();
- LockScreenActivity.this.overridePendingTransition(0, 0);
+// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ startActivity(intent);
+ // LockScreenActivity.this.overridePendingTransition(0, 0);
}
}
-
+ private void resetLanguage(String language) {
+ ((ImApp) getApplication()).setNewLocale(this, language);
+ Intent intent = getIntent();
+ intent.setClass(LockScreenActivity.this, LockScreenActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ finish();
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ overridePendingTransition(0, 0);
+ }
}
\ No newline at end of file
diff --git a/src/info/guardianproject/otr/app/im/app/Markup.java b/src/info/guardianproject/otr/app/im/app/Markup.java
index 7c3412ff1..2e66d7845 100644
--- a/src/info/guardianproject/otr/app/im/app/Markup.java
+++ b/src/info/guardianproject/otr/app/im/app/Markup.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/MessageView.java b/src/info/guardianproject/otr/app/im/app/MessageView.java
index 5dbdccd51..ca6f4fd8d 100644
--- a/src/info/guardianproject/otr/app/im/app/MessageView.java
+++ b/src/info/guardianproject/otr/app/im/app/MessageView.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -19,308 +19,799 @@
import info.guardianproject.emoji.EmojiManager;
import info.guardianproject.otr.app.im.R;
+import info.guardianproject.otr.app.im.engine.Presence;
+import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress;
import info.guardianproject.otr.app.im.provider.Imps;
+import info.guardianproject.otr.app.im.ui.ImageViewActivity;
+import info.guardianproject.otr.app.im.ui.LetterAvatar;
+import info.guardianproject.otr.app.im.ui.RoundedAvatarDrawable;
+import info.guardianproject.util.AudioPlayer;
+import info.guardianproject.util.LinkifyHelper;
+import info.guardianproject.util.LogCleaner;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.provider.Browser;
+import android.provider.MediaStore;
import android.support.v4.util.LruCache;
import android.text.Spannable;
import android.text.SpannableString;
-import android.text.style.ForegroundColorSpan;
+import android.text.TextUtils;
+import android.text.style.ClickableSpan;
+import android.text.style.ImageSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
-import android.view.Gravity;
+import android.util.Log;
import android.view.View;
+import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
import android.widget.TextView;
+import android.widget.Toast;
+
+public class MessageView extends FrameLayout {
+
+ private static int sCacheSize = 10; // 1MiB
+ private static LruCache mBitmapCache = new LruCache(sCacheSize);
-public class MessageView extends LinearLayout {
public enum DeliveryState {
NEUTRAL, DELIVERED, UNDELIVERED
}
public enum EncryptionState {
NONE, ENCRYPTED, ENCRYPTED_AND_VERIFIED
-
+
}
- private View mMessageContainer;
- private TextView mTextViewForMessages;
- private TextView mTextViewForTimestamp;
-
- private ImageView mDeliveryIcon;
- private Resources mResources;
- private ImageView mAvatarLeft;
- private ImageView mAvatarRight;
-
private CharSequence lastMessage = null;
-
- private static final int cacheSize = 10; // 4MiB
- private static LruCache bitmapCache = new LruCache(cacheSize);
- private static Drawable mAvatarUnknown;
-
+ private Context context;
+ private boolean linkify = false;
+
public MessageView(Context context, AttributeSet attrs) {
super(context, attrs);
-
- if (mAvatarUnknown == null)
- mAvatarUnknown = context.getResources().getDrawable(R.drawable.avatar_unknown);
+ this.context = context;
+ }
+
+ private ViewHolder mHolder = null;
+
+ private final static DateFormat MESSAGE_DATETIME_FORMAT = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+ private final static DateFormat MESSAGE_TIME_FORMAT = SimpleDateFormat.getTimeInstance(DateFormat.SHORT);
+ private static final SimpleDateFormat FMT_SAME_DAY = new SimpleDateFormat("yyyyMMdd");
+
+ private final static Date DATE_NOW = new Date();
+
+ private final static char DELIVERED_SUCCESS = '\u2714';
+ private final static char DELIVERED_FAIL = '\u2718';
+ private final static String LOCK_CHAR = "Secure";
+
+
+ class ViewHolder
+ {
+ TextView mTextViewForMessages = (TextView) findViewById(R.id.message);
+ TextView mTextViewForTimestamp = (TextView) findViewById(R.id.messagets);
+ ImageView mAvatar = (ImageView) findViewById(R.id.avatar);
+ // View mStatusBlock = findViewById(R.id.status_block);
+ ImageView mMediaThumbnail = (ImageView) findViewById(R.id.media_thumbnail);
+ View mContainer = findViewById(R.id.message_container);
+ // save the media uri while the MediaScanner is creating the thumbnail
+ // if the holder was reused, the pair is broken
+ Uri mMediaUri = null;
+
+ ViewHolder() {
+ // disable built-in autoLink so we can add custom ones
+ mTextViewForMessages.setAutoLinkMask(0);
+ }
+
+ public void setOnClickListenerMediaThumbnail( final String mimeType, final Uri mediaUri ) {
+ mMediaThumbnail.setOnClickListener( new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onClickMediaIcon( mimeType, mediaUri );
+ }
+ });
+ mMediaThumbnail.setOnLongClickListener( new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ onLongClickMediaIcon( mimeType, mediaUri );
+ return false;
+ }
+ });
+ }
+
+ public void resetOnClickListenerMediaThumbnail() {
+ mMediaThumbnail.setOnClickListener( null );
+ }
+
+ long mTimeDiff = -1;
+ }
+
+ /**
+ * This trickery is needed in order to have clickable links that open things
+ * in a new {@code Task} rather than in ChatSecure's {@code Task.} Thanks to @commonsware
+ * https://stackoverflow.com/a/11417498
+ *
+ */
+ class NewTaskUrlSpan extends ClickableSpan {
+
+ private String urlString;
+
+ NewTaskUrlSpan(String urlString) {
+ this.urlString = urlString;
+ }
+
+ @Override
+ public void onClick(View widget) {
+ Uri uri = Uri.parse(urlString);
+ Context context = widget.getContext();
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+
+ class URLSpanConverter implements LinkifyHelper.SpanConverter {
+ @Override
+ public NewTaskUrlSpan convert(URLSpan span) {
+ return (new NewTaskUrlSpan(span.getURL()));
+ }
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mMessageContainer = findViewById (R.id.message_container);
- mTextViewForMessages = (TextView) findViewById(R.id.message);
- mTextViewForTimestamp = (TextView) findViewById(R.id.messagets);
- mDeliveryIcon = (ImageView) findViewById(R.id.iconView);
- mAvatarLeft = (ImageView) findViewById(R.id.avatar_left);
- mAvatarRight = (ImageView) findViewById(R.id.avatar_right);
-
- mResources = getResources();
-
+ mHolder = (ViewHolder)getTag();
+
+ if (mHolder == null)
+ {
+ mHolder = new ViewHolder();
+ setTag(mHolder);
+
+ }
+ }
+
+ public void setLinkify(boolean linkify) {
+ this.linkify = linkify;
+ }
+
+ public void setMessageBackground (Drawable d) {
+ mHolder.mContainer.setBackgroundDrawable(d);
}
public URLSpan[] getMessageLinks() {
- return mTextViewForMessages.getUrls();
+ return mHolder.mTextViewForMessages.getUrls();
}
-
+
public String getLastMessage () {
return lastMessage.toString();
}
- public void bindIncomingMessage(String address, String nickname, String body, Date date, Markup smileyRes,
- boolean scrolling, EncryptionState encryption, boolean showContact) {
-
- ListView.LayoutParams lp = new ListView.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- setGravity(Gravity.LEFT);
- setLayoutParams(lp);
- setPadding(3,0,100,3);
-
- showAvatar(address,true);
-
- if (showContact)
+
+ public void bindIncomingMessage(int id, int messageType, String address, String nickname, final String mimeType, final String body, Date date, Markup smileyRes,
+ boolean scrolling, EncryptionState encryption, boolean showContact, int presenceStatus) {
+
+ mHolder = (ViewHolder)getTag();
+
+ mHolder.mTextViewForMessages.setVisibility(View.VISIBLE);
+
+ if (nickname == null)
+ nickname = address;
+
+ if (showContact && nickname != null)
{
- String[] nickParts = nickname.split("/");
-
- lastMessage = nickParts[nickParts.length-1] + ": " + formatMessage(body);
-
+ lastMessage = nickname + ": " + formatMessage(body);
+ showAvatar(address,nickname,true,presenceStatus);
}
else
{
lastMessage = formatMessage(body);
- }
-
+ showAvatar(address,nickname,true,presenceStatus);
+
+ mHolder.resetOnClickListenerMediaThumbnail();
+ if( mimeType != null ) {
+
+ mHolder.mTextViewForMessages.setVisibility(View.GONE);
+ mHolder.mMediaThumbnail.setVisibility(View.VISIBLE);
+
+ Uri mediaUri = Uri.parse( body ) ;
+ lastMessage = "";
+ showMediaThumbnail(mimeType, mediaUri, id, mHolder);
+
+ } else {
+ mHolder.mMediaThumbnail.setVisibility(View.GONE);
+ if (showContact)
+ {
+ String[] nickParts = nickname.split("/");
+
+ lastMessage = nickParts[nickParts.length-1] + ": " + formatMessage(body);
+
+ }
+ else
+ {
+ lastMessage = formatMessage(body);
+ }
+ }
+ }
+
if (lastMessage.length() > 0)
{
try {
SpannableString spannablecontent=new SpannableString(lastMessage);
EmojiManager.getInstance(getContext()).addEmoji(getContext(), spannablecontent);
-
- mTextViewForMessages.setText(spannablecontent);
+
+ mHolder.mTextViewForMessages.setText(spannablecontent);
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ LogCleaner.error(ImApp.LOG_TAG, "error processing message", e);
}
}
else
{
- mTextViewForMessages.setText(lastMessage);
+ mHolder.mTextViewForMessages.setText(lastMessage);
}
-
-
- mDeliveryIcon.setVisibility(INVISIBLE);
-
+
+
if (date != null)
{
- CharSequence tsText = formatTimeStamp(date);
-
- mTextViewForTimestamp.setText(tsText);
- mTextViewForTimestamp.setGravity(Gravity.CENTER);
- mTextViewForTimestamp.setVisibility(View.VISIBLE);
-
+ CharSequence tsText = null;
+
+ if (isSameDay(date,DATE_NOW))
+ tsText = formatTimeStamp(date,messageType,MESSAGE_TIME_FORMAT, null, encryption);
+ else
+ tsText = formatTimeStamp(date,messageType,MESSAGE_DATETIME_FORMAT, null, encryption);
+
+ mHolder.mTextViewForTimestamp.setText(tsText);
+ mHolder.mTextViewForTimestamp.setVisibility(View.VISIBLE);
+
}
else
{
-
- mTextViewForTimestamp.setText("");
- mTextViewForTimestamp.setVisibility(View.GONE);
-
+
+ mHolder.mTextViewForTimestamp.setText("");
+ //mHolder.mTextViewForTimestamp.setVisibility(View.GONE);
+
}
-
- if (encryption == EncryptionState.NONE)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_plaintext);
- else if (encryption == EncryptionState.ENCRYPTED)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_encrypted);
- else if (encryption == EncryptionState.ENCRYPTED_AND_VERIFIED)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_verified);
-
- mTextViewForMessages.setTextColor(getResources().getColor(R.color.incoming_message_fg));
-
+ if (linkify)
+ LinkifyHelper.addLinks(mHolder.mTextViewForMessages, new URLSpanConverter());
+ LinkifyHelper.addTorSafeLinks(mHolder.mTextViewForMessages);
+ }
+
+ private void showMediaThumbnail (String mimeType, Uri mediaUri, int id, ViewHolder holder)
+ {
+ /* Guess the MIME type in case we received a file that we can display or play*/
+ if (TextUtils.isEmpty(mimeType) || mimeType.startsWith("application")) {
+ String guessed = URLConnection.guessContentTypeFromName(mediaUri.toString());
+ if (!TextUtils.isEmpty(guessed)) {
+ if (TextUtils.equals(guessed, "video/3gpp"))
+ mimeType = "audio/3gpp";
+ else
+ mimeType = guessed;
+ }
+ }
+ holder.setOnClickListenerMediaThumbnail(mimeType, mediaUri);
+
+ holder.mMediaThumbnail.setVisibility(View.VISIBLE);
+ holder.mTextViewForMessages.setText(lastMessage);
+ holder.mTextViewForMessages.setVisibility(View.GONE);
+
+ if( mimeType.startsWith("image/") ) {
+ setImageThumbnail( getContext().getContentResolver(), id, holder, mediaUri );
+ holder.mMediaThumbnail.setBackgroundColor(Color.TRANSPARENT);
+ // holder.mMediaThumbnail.setBackgroundColor(Color.WHITE);
+
+ }
+ else if (mimeType.startsWith("audio"))
+ {
+ holder.mMediaThumbnail.setImageResource(R.drawable.media_audio_play);
+ holder.mMediaThumbnail.setBackgroundColor(Color.TRANSPARENT);
+ }
+ else
+ {
+ holder.mMediaThumbnail.setImageResource(R.drawable.ic_file); // generic file icon
+
+ }
+
+ holder.mContainer.setBackgroundColor(getResources().getColor(android.R.color.transparent));
+
+
}
- private String formatMessage (String body)
+
+ private boolean isSameDay (Date date1, Date date2)
{
- return android.text.Html.fromHtml(body).toString();
+ return FMT_SAME_DAY.format(date1).equals(FMT_SAME_DAY.format(date2));
}
-
- public void bindOutgoingMessage(String address, String body, Date date, Markup smileyRes, boolean scrolling,
- DeliveryState delivery, EncryptionState encryption) {
-
-
-
- ListView.LayoutParams lp = new ListView.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- // lp.setMargins(3,3,100,3);
- setLayoutParams(lp);
- setGravity(Gravity.RIGHT);
- setPadding(100, 0, 3, 3);
-
- showAvatar(address,false);
+ protected String convertMediaUriToPath(Uri uri) {
+ String path = null;
+
+ String [] proj={MediaStore.Images.Media.DATA};
+ Cursor cursor = getContext().getContentResolver().query(uri, proj, null, null, null);
+ if (cursor != null && (!cursor.isClosed()))
+ {
+ if (cursor.isBeforeFirst())
+ {
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ cursor.moveToFirst();
+ path = cursor.getString(column_index);
+ }
+
+ cursor.close();
+ }
+
+ return path;
+ }
+
+ private MediaPlayer mMediaPlayer = null;
+
+ /**
+ * @param mimeType
+ * @param body
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ protected void onClickMediaIcon(String mimeType, Uri mediaUri) {
+
+ if (ChatFileStore.isVfsUri(mediaUri)) {
+ if (mimeType.startsWith("image")) {
+ Intent intent = new Intent(context, ImageViewActivity.class);
+ intent.putExtra( ImageViewActivity.FILENAME, mediaUri.getPath());
+ context.startActivity(intent);
+ return;
+ }
+ if (mimeType.startsWith("audio")) {
+ new AudioPlayer(getContext(), mediaUri.getPath(), mimeType).play();
+ return;
+ }
+ return;
+ }
+ else
+ {
+
+
+ String body = convertMediaUriToPath(mediaUri);
+
+ if (body == null)
+ body = new File(mediaUri.getPath()).getAbsolutePath();
+
+ if (mimeType.startsWith("audio") || (body.endsWith("3gp")||body.endsWith("3gpp")||body.endsWith("amr")))
+ {
+
+ if (mMediaPlayer != null)
+ mMediaPlayer.release();
+
+ try
+ {
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.setDataSource(body);
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+
+ return;
+ } catch (IOException e) {
+ Log.e(ImApp.LOG_TAG,"error playing audio: " + body,e);
+ }
+
+
+ }
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (Build.VERSION.SDK_INT >= 11)
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ //set a general mime type not specific
+ intent.setDataAndType(Uri.parse( body ), mimeType);
+
+ Context context = getContext().getApplicationContext();
+
+ if (isIntentAvailable(context,intent))
+ {
+ context.startActivity(intent);
+ }
+ else
+ {
+ Toast.makeText(getContext(), R.string.there_is_no_viewer_available_for_this_file_format, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ protected void onLongClickMediaIcon(final String mimeType, final Uri mediaUri) {
+
+ final java.io.File exportPath = ChatFileStore.exportPath(mimeType, mediaUri);
+
+ new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.export_media))
+ .setMessage(context.getString(R.string.export_media_file_to, exportPath.getAbsolutePath()))
+ .setPositiveButton(R.string.export, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ try {
+ ChatFileStore.exportContent(mimeType, mediaUri, exportPath);
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportPath));
+ shareIntent.setType(mimeType);
+ context.startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.export_media)));
+ } catch (IOException e) {
+ Toast.makeText(getContext(), "Export Failed " + e.getMessage(), Toast.LENGTH_LONG).show();
+ e.printStackTrace();
+ }
+ return;
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ return;
+ }
+ })
+ .create().show();
+ }
+
+ public static boolean isIntentAvailable(Context context, Intent intent) {
+ final PackageManager packageManager = context.getPackageManager();
+ List list =
+ packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+
+
+ /**
+ * @param contentResolver
+ * @param id
+ * @param aHolder
+ * @param mediaUri
+ */
+ private void setImageThumbnail(final ContentResolver contentResolver, final int id, final ViewHolder aHolder, final Uri mediaUri) {
+ // pair this holder to the uri. if the holder is recycled, the pairing is broken
+ aHolder.mMediaUri = mediaUri;
+ // if a content uri - already scanned
+
+ setThumbnail(contentResolver, aHolder, mediaUri);
+
+
+ }
+
+ /**
+ * @param contentResolver
+ * @param aHolder
+ * @param uri
+ */
+ private void setThumbnail(final ContentResolver contentResolver, final ViewHolder aHolder, final Uri uri) {
+ new AsyncTask() {
+
+ @Override
+ protected Bitmap doInBackground(String... params) {
+
+ Bitmap result = mBitmapCache.get(uri.toString());
+
+ if (result == null)
+ return getThumbnail( contentResolver, uri );
+ else
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+
+ if (uri != null && result != null)
+ {
+ mBitmapCache.put(uri.toString(), result);
+
+ // confirm the holder is still paired to this uri
+ if( ! uri.equals( aHolder.mMediaUri ) ) {
+ return ;
+ }
+ // set the thumbnail
+ aHolder.mMediaThumbnail.setImageBitmap(result);
+ }
+ }
+ }.execute();
+ }
+
+ public final static int THUMBNAIL_SIZE_DEFAULT = 400;
+
+ public static Bitmap getThumbnail(ContentResolver cr, Uri uri) {
+ // Log.e( MessageView.class.getSimpleName(), "getThumbnail uri:" + uri);
+ if (ChatFileStore.isVfsUri(uri)) {
+ return ChatFileStore.getThumbnailVfs(uri, THUMBNAIL_SIZE_DEFAULT);
+ }
+ return getThumbnailFile(cr, uri, THUMBNAIL_SIZE_DEFAULT);
+ }
+
+ public static Bitmap getThumbnailFile(ContentResolver cr, Uri uri, int thumbnailSize) {
+
+
+ try
+ {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ options.inInputShareable = true;
+ options.inPurgeable = true;
-
- lastMessage = body;//formatMessage(body);
-
- try {
- SpannableString spannablecontent=new SpannableString(lastMessage);
-
- EmojiManager.getInstance(getContext()).addEmoji(getContext(), spannablecontent);
-
- mTextViewForMessages.setText(spannablecontent);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ InputStream is = cr.openInputStream(uri);
+ BitmapFactory.decodeStream(is, null, options);
+ if ((options.outWidth == -1) || (options.outHeight == -1))
+ return null;
+
+ int originalSize = (options.outHeight > options.outWidth) ? options.outHeight
+ : options.outWidth;
+
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inSampleSize = originalSize / thumbnailSize;
+
+ is = cr.openInputStream(uri);
- if (delivery == DeliveryState.DELIVERED) {
- mDeliveryIcon.setImageResource(R.drawable.ic_chat_msg_status_ok);
- mDeliveryIcon.setVisibility(VISIBLE);
- } else if (delivery == DeliveryState.UNDELIVERED) {
- mDeliveryIcon.setImageResource(R.drawable.ic_chat_msg_status_failed);
- mDeliveryIcon.setVisibility(VISIBLE);
+ Bitmap scaledBitmap = BitmapFactory.decodeStream(is, null, options);
+
+ return scaledBitmap;
+ }
+ catch (Exception e)
+ {
+ Log.d(ImApp.LOG_TAG,"could not getThumbnailFile",e);
+ return null;
+ }
+ }
+
+ private String formatMessage (String body)
+ {
+ if (body != null)
+ return android.text.Html.fromHtml(body).toString();
+ else
+ return null;
+ }
+
+ public void bindOutgoingMessage(int id, int messageType, String address, final String mimeType, final String body, Date date, Markup smileyRes, boolean scrolling,
+ DeliveryState delivery, EncryptionState encryption) {
+
+ mHolder = (ViewHolder)getTag();
+
+ mHolder.mTextViewForMessages.setVisibility(View.VISIBLE);
+ mHolder.resetOnClickListenerMediaThumbnail();
+ if( mimeType != null ) {
+
+ lastMessage = "";
+ Uri mediaUri = Uri.parse( body ) ;
+
+ showMediaThumbnail(mimeType, mediaUri, id, mHolder);
+
+
+ mHolder.mTextViewForMessages.setVisibility(View.GONE);
+ mHolder.mMediaThumbnail.setVisibility(View.VISIBLE);
+
} else {
- mDeliveryIcon.setVisibility(GONE);
+ mHolder.mMediaThumbnail.setVisibility(View.GONE);
+ lastMessage = body;//formatMessage(body);
+
+ try {
+
+ SpannableString spannablecontent=new SpannableString(lastMessage);
+
+ EmojiManager.getInstance(getContext()).addEmoji(getContext(), spannablecontent);
+
+ mHolder.mTextViewForMessages.setText(spannablecontent);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
-
if (date != null)
{
- mTextViewForTimestamp.setText(formatTimeStamp(date));
- mTextViewForTimestamp.setGravity(Gravity.CENTER);
- mTextViewForTimestamp.setVisibility(View.VISIBLE);
- mTextViewForTimestamp.setPadding(0,0,0,12);
+
+ CharSequence tsText = null;
+
+ if (isSameDay(date,DATE_NOW))
+ tsText = formatTimeStamp(date,messageType, MESSAGE_TIME_FORMAT, delivery, encryption);
+ else
+ tsText = formatTimeStamp(date,messageType, MESSAGE_DATETIME_FORMAT, delivery, encryption);
+
+ mHolder.mTextViewForTimestamp.setText(tsText);
+
+ mHolder.mTextViewForTimestamp.setVisibility(View.VISIBLE);
}
else
{
- mTextViewForTimestamp.setText("");
- mTextViewForTimestamp.setVisibility(View.GONE);
- mTextViewForTimestamp.setPadding(0,0,0,0);
+ mHolder.mTextViewForTimestamp.setText("");
}
-
- if (encryption == EncryptionState.NONE)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_plaintext);
- else if (encryption == EncryptionState.ENCRYPTED)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_encrypted);
- else if (encryption == EncryptionState.ENCRYPTED_AND_VERIFIED)
- mMessageContainer.setBackgroundResource(R.color.incoming_message_bg_verified);
-
- mTextViewForMessages.setTextColor(getResources().getColor(R.color.outgoing_message_fg));
+ if (linkify)
+ LinkifyHelper.addLinks(mHolder.mTextViewForMessages, new URLSpanConverter());
+ LinkifyHelper.addTorSafeLinks(mHolder.mTextViewForMessages);
}
- private void showAvatar (String address, boolean isLeft)
+ private void showAvatar (String address, String nickname, boolean isLeft, int presenceStatus)
{
+ if (mHolder.mAvatar == null)
+ return;
- mAvatarLeft.setVisibility(View.GONE);
- mAvatarRight.setVisibility(View.GONE);
-
- if (address != null)
+ mHolder.mAvatar.setVisibility(View.GONE);
+
+ if (address != null && isLeft)
{
- Drawable avatar = ContactView.getAvatar(address);
-
- if (avatar == null)
- {
- avatar = mAvatarUnknown;
-
- }
-
-
- if (isLeft)
+
+ RoundedAvatarDrawable avatar = null;
+
+ try { avatar = DatabaseUtils.getAvatarFromAddress(this.getContext().getContentResolver(),XmppAddress.stripResource(address), ImApp.DEFAULT_AVATAR_WIDTH,ImApp.DEFAULT_AVATAR_HEIGHT);}
+ catch (Exception e){}
+
+ if (avatar != null)
{
- mAvatarLeft.setVisibility(View.VISIBLE);
- mAvatarLeft.setImageDrawable(avatar);
+ mHolder.mAvatar.setVisibility(View.VISIBLE);
+ mHolder.mAvatar.setImageDrawable(avatar);
+
+ setAvatarBorder(presenceStatus, avatar);
+
}
else
{
- mAvatarRight.setVisibility(View.VISIBLE);
- mAvatarRight.setImageDrawable(avatar);
+ int color = getAvatarBorder(presenceStatus);
+ int padding = 16;
+ LetterAvatar lavatar = new LetterAvatar(getContext(), color, nickname.substring(0,1).toUpperCase(), padding);
+
+ mHolder.mAvatar.setVisibility(View.VISIBLE);
+ mHolder.mAvatar.setImageDrawable(lavatar);
}
- }
+ }
+ }
+
+ public int getAvatarBorder(int status) {
+ switch (status) {
+ case Presence.AVAILABLE:
+ return (getResources().getColor(R.color.holo_green_light));
+
+ case Presence.IDLE:
+ return (getResources().getColor(R.color.holo_green_dark));
+ case Presence.AWAY:
+ return (getResources().getColor(R.color.holo_orange_light));
+
+ case Presence.DO_NOT_DISTURB:
+ return(getResources().getColor(R.color.holo_red_dark));
+
+ case Presence.OFFLINE:
+ return(getResources().getColor(R.color.holo_grey_dark));
+
+ default:
+ }
+
+ return Color.TRANSPARENT;
}
+
public void bindPresenceMessage(String contact, int type, boolean isGroupChat, boolean scrolling) {
+
+ mHolder = (ViewHolder)getTag();
+
CharSequence message = formatPresenceUpdates(contact, type, isGroupChat, scrolling);
- mTextViewForMessages.setText(message);
- mTextViewForMessages.setTextColor(mResources.getColor(R.color.chat_msg_presence));
- mDeliveryIcon.setVisibility(INVISIBLE);
+ mHolder.mTextViewForMessages.setText(message);
+ // mHolder.mTextViewForMessages.setTextColor(getResources().getColor(R.color.chat_msg_presence));
+
}
public void bindErrorMessage(int errCode) {
- mTextViewForMessages.setText(R.string.msg_sent_failed);
- mTextViewForMessages.setTextColor(mResources.getColor(R.color.error));
- mDeliveryIcon.setVisibility(INVISIBLE);
+
+ mHolder = (ViewHolder)getTag();
+
+ mHolder.mTextViewForMessages.setText(R.string.msg_sent_failed);
+ mHolder.mTextViewForMessages.setTextColor(getResources().getColor(R.color.error));
+
}
-
+ private SpannableString formatTimeStamp(Date date, int messageType, DateFormat format, MessageView.DeliveryState delivery, EncryptionState encryptionState) {
+
+
+ StringBuilder deliveryText = new StringBuilder();
+ deliveryText.append(format.format(date));
+ deliveryText.append(' ');
+
+ if (delivery != null)
+ {
+ //this is for delivery
+ if (delivery == DeliveryState.DELIVERED) {
+
+ deliveryText.append(DELIVERED_SUCCESS);
+
+ } else if (delivery == DeliveryState.UNDELIVERED) {
- private SpannableString formatTimeStamp(Date date) {
- // DateFormat format = new SimpleDateFormat(mResources.getString(R.string.time_stamp));
+ deliveryText.append(DELIVERED_FAIL);
+ }
+ }
- DateFormat format = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
- String dateStr = format.format(date);
- SpannableString spanText = new SpannableString(dateStr);
- int len = spanText.length();
- spanText.setSpan(new StyleSpan(Typeface.ITALIC), 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- spanText.setSpan(new RelativeSizeSpan(0.8f), 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- spanText.setSpan(new ForegroundColorSpan(mResources.getColor(android.R.color.darker_gray)),
- 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
+ if (messageType != Imps.MessageType.POSTPONED)
+ deliveryText.append(DELIVERED_SUCCESS);//this is for sent, so we know show 2 checks like WhatsApp!
+
+ SpannableString spanText = null;
+
+ if (encryptionState == EncryptionState.ENCRYPTED)
+ {
+ deliveryText.append('X');
+ spanText = new SpannableString(deliveryText.toString());
+ int len = spanText.length();
+
+ spanText.setSpan(new ImageSpan(getContext(), R.drawable.lock16), len-1,len,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ }
+ else if (encryptionState == EncryptionState.ENCRYPTED_AND_VERIFIED)
+ {
+ deliveryText.append('X');
+ spanText = new SpannableString(deliveryText.toString());
+ int len = spanText.length();
+
+ spanText.setSpan(new ImageSpan(getContext(), R.drawable.lock16), len-1,len,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ }
+ else
+ {
+ spanText = new SpannableString(deliveryText.toString());
+ int len = spanText.length();
+
+ }
+
+ // spanText.setSpan(new StyleSpan(Typeface.SANS_SERIF), 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ // spanText.setSpan(new RelativeSizeSpan(0.8f), 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ // spanText.setSpan(new ForegroundColorSpan(R.color.soft_grey),
+ // 0, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
return spanText;
}
private CharSequence formatPresenceUpdates(String contact, int type, boolean isGroupChat,
boolean scrolling) {
String body;
+
+ Resources resources =getResources();
+
switch (type) {
case Imps.MessageType.PRESENCE_AVAILABLE:
- body = mResources.getString(isGroupChat ? R.string.contact_joined
+ body = resources.getString(isGroupChat ? R.string.contact_joined
: R.string.contact_online, contact);
break;
case Imps.MessageType.PRESENCE_AWAY:
- body = mResources.getString(R.string.contact_away, contact);
+ body = resources.getString(R.string.contact_away, contact);
break;
case Imps.MessageType.PRESENCE_DND:
- body = mResources.getString(R.string.contact_busy, contact);
+ body = resources.getString(R.string.contact_busy, contact);
break;
case Imps.MessageType.PRESENCE_UNAVAILABLE:
- body = mResources.getString(isGroupChat ? R.string.contact_left
+ body = resources.getString(isGroupChat ? R.string.contact_left
: R.string.contact_offline, contact);
break;
@@ -340,4 +831,38 @@ private CharSequence formatPresenceUpdates(String contact, int type, boolean isG
return spanText;
}
}
+
+ public void setAvatarBorder(int status, RoundedAvatarDrawable avatar) {
+ switch (status) {
+ case Presence.AVAILABLE:
+ avatar.setBorderColor(getResources().getColor(R.color.holo_green_light));
+ avatar.setAlpha(255);
+ break;
+
+ case Presence.IDLE:
+ avatar.setBorderColor(getResources().getColor(R.color.holo_green_dark));
+ avatar.setAlpha(255);
+
+ break;
+
+ case Presence.AWAY:
+ avatar.setBorderColor(getResources().getColor(R.color.holo_orange_light));
+ avatar.setAlpha(255);
+ break;
+
+ case Presence.DO_NOT_DISTURB:
+ avatar.setBorderColor(getResources().getColor(R.color.holo_red_dark));
+ avatar.setAlpha(255);
+
+ break;
+
+ case Presence.OFFLINE:
+ avatar.setBorderColor(getResources().getColor(R.color.holo_grey_light));
+ avatar.setAlpha(150);
+ break;
+
+
+ default:
+ }
+ }
}
diff --git a/src/info/guardianproject/otr/app/im/app/MissingChatFileStoreActivity.java b/src/info/guardianproject/otr/app/im/app/MissingChatFileStoreActivity.java
new file mode 100644
index 000000000..c778d8385
--- /dev/null
+++ b/src/info/guardianproject/otr/app/im/app/MissingChatFileStoreActivity.java
@@ -0,0 +1,64 @@
+package info.guardianproject.otr.app.im.app;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import info.guardianproject.otr.app.im.R;
+
+import java.io.File;
+
+public class MissingChatFileStoreActivity extends ThemeableActivity {
+ private static final String TAG = "MissingChatFileStoreActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().hide();
+ setContentView(R.layout.missing_chat_file_store);
+
+ TextView titleTextView = (TextView) findViewById(R.id.title);
+ TextView messageTextView = (TextView) findViewById(R.id.message);
+ Button deleteChatLogButton = (Button) findViewById(R.id.delete_chat_log);
+ Button shutdownAndLockButton = (Button) findViewById(R.id.shutdown_and_exit);
+
+ if (getExternalFilesDir(null) == null) {
+ titleTextView.setText(R.string.external_storage_missing_title);
+ messageTextView.setText(R.string.external_storage_missing_message);
+ } else {
+ titleTextView.setText(R.string.media_store_file_missing_title);
+ messageTextView.setText(R.string.media_store_file_missing_message);
+ }
+ deleteChatLogButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "init try again onClick");
+ Context c = getApplicationContext();
+ new File(ChatFileStore.getInternalDbFilePath(c)).delete();
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(c);
+ Editor editor = settings.edit();
+ editor.putBoolean(getString(R.string.key_store_media_on_external_storage_pref),
+ false);
+ editor.apply();
+ finish();
+ }
+ });
+ shutdownAndLockButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "shutdownAndLock onClick");
+ WelcomeActivity.shutdownAndLock(MissingChatFileStoreActivity.this);
+ }
+ });
+ }
+
+}
diff --git a/src/info/guardianproject/otr/app/im/app/NetworkConnectivityListener.java b/src/info/guardianproject/otr/app/im/app/NetworkConnectivityListener.java
index c0cf90d3a..0708f46b1 100644
--- a/src/info/guardianproject/otr/app/im/app/NetworkConnectivityListener.java
+++ b/src/info/guardianproject/otr/app/im/app/NetworkConnectivityListener.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2006 The Android Open Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -33,8 +33,8 @@
/**
* A wrapper for a broadcast receiver that provides network connectivity state
* information, independent of network type (mobile, Wi-Fi, etc.). {@hide
- *
- *
+ *
+ *
* }
*/
public class NetworkConnectivityListener extends BroadcastReceiver {
@@ -55,43 +55,54 @@ public class NetworkConnectivityListener extends BroadcastReceiver {
* established, or may be attempting to establish, connectivity with another
* network. If so, {@code mOtherNetworkInfo} will be non-null.
*/
- private NetworkInfo mOtherNetworkInfo;
+ // private NetworkInfo mOtherNetworkInfo;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
-
+
/*
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || mListening == false) {
Log.w(TAG, "onReceived() called with " + mState.toString() + " and " + intent);
return;
}*/
-
+
boolean noConnectivity = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- mNetworkInfo = (NetworkInfo) intent
- .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
- mOtherNetworkInfo = (NetworkInfo) intent
- .getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
-
- mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
- mIsFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
-
- //let's just check the state of our active network to set this value
- if (isNetworkAvailableAndConnected(context.getApplicationContext())) {
- mState = State.CONNECTED;
- } else {
+ if (noConnectivity)
+ {
mState = State.NOT_CONNECTED;
}
-
+ else
+ {
+ ConnectivityManager manager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Getting from intent is deprecated - get from manager
+ mNetworkInfo = manager.getActiveNetworkInfo();
+ // mOtherNetworkInfo = (NetworkInfo) intent
+ // .getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+
+ mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
+ mIsFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
+
+ if (mNetworkInfo != null && mNetworkInfo.isConnected())
+ {
+ mState = State.CONNECTED;
+
+ }
+ else {
+ mState = State.NOT_CONNECTED;
+ }
+ }
+
/*
Log.d(TAG, "onReceive(): mNetworkInfo="
utoConnect + mNetworkInfo
+ " mOtherNetworkInfo = "
+ (mOtherNetworkInfo == null ? "[none]" : mOtherNetworkInfo + " noConn="
+ noConnectivity) + " mState=" + mState);*/
-
+
if (mHandlers != null)
{
// Notifiy any handlers.
@@ -103,9 +114,9 @@ public void onReceive(Context context, Intent intent) {
}
}
}
-
- public enum State {
+
+ public static enum State {
UNKNOWN,
/** This state is returned if there is connectivity to any network **/
@@ -128,7 +139,7 @@ public NetworkConnectivityListener() {
/**
* This method starts listening for network connectivity state changes.
- *
+ *
* @param context
*/
public synchronized void startListening(Context context) {
@@ -148,7 +159,7 @@ public synchronized void stopListening() {
mContext.unregisterReceiver(this);
mContext = null;
mNetworkInfo = null;
- mOtherNetworkInfo = null;
+ // mOtherNetworkInfo = null;
mIsFailover = false;
mReason = null;
mListening = false;
@@ -158,7 +169,7 @@ public synchronized void stopListening() {
/**
* This methods registers a Handler to be called back onto with the
* specified what code when the network connectivity state changes.
- *
+ *
* @param target The target handler.
* @param what The what code to be used when posting a message to the
* handler.
@@ -169,7 +180,7 @@ public static void registerHandler(Handler target, int what) {
/**
* This methods unregisters the specified Handler.
- *
+ *
* @param target
*/
public static void unregisterHandler(Handler target) {
@@ -183,7 +194,7 @@ public State getState() {
/**
* Return the NetworkInfo associated with the most recent connectivity
* event.
- *
+ *
* @return {@code NetworkInfo} for the network that had the most recent
* connectivity event.
*/
@@ -197,17 +208,18 @@ public NetworkInfo getNetworkInfo() {
* might be available. If this returns a non-null value, then another
* broadcast should follow shortly indicating whether connection to the
* other network succeeded.
- *
+ *
* @return NetworkInfo
*/
+ /**
public NetworkInfo getOtherNetworkInfo() {
return mOtherNetworkInfo;
- }
+ }*/
/**
* Returns true if the most recent event was for an attempt to switch over
* to a new network following loss of connectivity on another network.
- *
+ *
* @return {@code true} if this was a failover attempt, {@code false}
* otherwise.
*/
@@ -218,26 +230,12 @@ public boolean isFailover() {
/**
* An optional reason for the connectivity state change may have been
* supplied. This returns it.
- *
+ *
* @return the reason for the state change, if available, or {@code null}
* otherwise.
*/
public String getReason() {
return mReason;
}
-
- public boolean isNetworkAvailableAndConnected (Context context) {
- ConnectivityManager manager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
-
- NetworkInfo nInfo = manager.getActiveNetworkInfo();
- if (nInfo != null)
- {
- Log.d(ImApp.LOG_TAG,"network state: available=" + nInfo.isAvailable() + " connected/connecting=" + nInfo.isConnectedOrConnecting());
- return nInfo.isAvailable() && nInfo.isConnectedOrConnecting();
- }
- else
- return false; //no network info is a bad idea
- }
}
diff --git a/src/info/guardianproject/otr/app/im/app/NewChatActivity.java b/src/info/guardianproject/otr/app/im/app/NewChatActivity.java
index 4d3530fc7..86fcb7724 100644
--- a/src/info/guardianproject/otr/app/im/app/NewChatActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/NewChatActivity.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -16,39 +16,84 @@
*/
package info.guardianproject.otr.app.im.app;
+import info.guardianproject.iocipher.VirtualFileSystem;
import info.guardianproject.otr.IOtrChatSession;
+import info.guardianproject.otr.OtrAndroidKeyManagerImpl;
+import info.guardianproject.otr.OtrChatManager;
import info.guardianproject.otr.app.im.IChatSession;
import info.guardianproject.otr.app.im.IChatSessionManager;
+import info.guardianproject.otr.app.im.IContactList;
+import info.guardianproject.otr.app.im.IContactListListener;
import info.guardianproject.otr.app.im.IContactListManager;
import info.guardianproject.otr.app.im.IImConnection;
+import info.guardianproject.otr.app.im.ISubscriptionListener;
import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.app.ContactListFilterView.ContactListListener;
-import info.guardianproject.otr.app.im.app.adapter.ChatListenerAdapter;
+import info.guardianproject.otr.app.im.engine.Contact;
import info.guardianproject.otr.app.im.engine.ImConnection;
+import info.guardianproject.otr.app.im.engine.ImErrorInfo;
+import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
+import info.guardianproject.otr.app.im.ui.SecureCameraActivity;
import info.guardianproject.util.LogCleaner;
-
+import info.guardianproject.util.SystemServices;
+import info.guardianproject.util.SystemServices.FileInfo;
+import info.guardianproject.util.XmppUriHelper;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+
+import net.java.otr4j.OtrPolicy;
+import net.java.otr4j.session.SessionStatus;
+
+import org.ironrabbit.type.CustomTypefaceManager;
+import org.jivesoftware.smackx.muc.MultiUserChat;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.SimpleOnPageChangeListener;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.widget.Toolbar;
+import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
+import android.text.SpannableString;
+import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
@@ -56,435 +101,991 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.view.Window;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
-import android.widget.CursorAdapter;
-import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
-import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
-
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
public class NewChatActivity extends FragmentActivity implements View.OnCreateContextMenuListener {
+ private static final String ICICLE_CHAT_PAGER_ADAPTER = "chatPagerAdapter";
+ private static final String ICICLE_POSITION = "position";
private static final int MENU_RESEND = Menu.FIRST;
+
private static final int REQUEST_PICK_CONTACTS = RESULT_FIRST_USER + 1;
+ private static final int REQUEST_SEND_IMAGE = REQUEST_PICK_CONTACTS + 1;
+ private static final int REQUEST_SEND_FILE = REQUEST_SEND_IMAGE + 1;
+ private static final int REQUEST_SEND_AUDIO = REQUEST_SEND_FILE + 1;
+ private static final int REQUEST_TAKE_PICTURE = REQUEST_SEND_AUDIO + 1;
+ private static final int REQUEST_SETTINGS = REQUEST_TAKE_PICTURE + 1;
+ private static final int REQUEST_TAKE_PICTURE_SECURE = REQUEST_SETTINGS + 1;
+ private static final int REQUEST_ADD_CONTACT = REQUEST_TAKE_PICTURE_SECURE + 1;
+
+ private static final int CONTACT_LIST_LOADER_ID = 4444;
+ private static final int CHAT_LIST_LOADER_ID = CONTACT_LIST_LOADER_ID+1;
+ private static final int CHAT_PAGE_LOADER_ID = CONTACT_LIST_LOADER_ID+2;
private ImApp mApp;
private ViewPager mChatPager;
- private static ChatViewPagerAdapter mChatPagerAdapter;
-
- private Cursor mCursorChats;
-
+ private ChatViewPagerAdapter mChatPagerAdapter;
+
+ private Toolbar mToolbar;
+ private DrawerLayout mDrawer;
+ private ActionBarDrawerToggle mDrawerToggle;
+
+ private int mLastPagePosition = -1;
+
private SimpleAlertHandler mHandler;
- private MenuItem menuOtr, menuCall;
-
- private LayoutInflater mInflater;
- private static long mAccountId = -1;
- private static long mLastProviderId = -1;
-
- private String mSipAccount = null;
-
+ private long mLastAccountId = -1;
+ private long mLastProviderId = -1;
+ private boolean mShowChatsOnly = true;
+
private MessageContextMenuHandler mMessageContextMenuHandler;
-
+
private ContactListFragment mContactList = null;
+ private Imps.ProviderSettings.QueryMap mGlobalSettings = null;
+
+ final static class MyHandler extends SimpleAlertHandler {
+ public MyHandler(NewChatActivity activity) {
+ super(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == ImApp.EVENT_SERVICE_CONNECTED) {
+ ((NewChatActivity)mActivity).onServiceConnected();
+ return;
+ }
+ super.handleMessage(msg);
+ }
+ }
+
+
@Override
protected void onCreate(Bundle icicle) {
-
+
super.onCreate(icicle);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.chat_pager);
+ checkCustomFont ();
- mChatPager = (ViewPager) findViewById(R.id.chatpager);
-
mApp = (ImApp)getApplication();
- mInflater = LayoutInflater.from(this);
-
- mMessageContextMenuHandler = new MessageContextMenuHandler();
+ mApp.maybeInit(this);
- initSideBar ();
-
- mChatPagerAdapter = new ChatViewPagerAdapter(getSupportFragmentManager());
- mChatPager.setAdapter(mChatPagerAdapter);
-
- new java.util.Timer().schedule(
- new java.util.TimerTask() {
- @Override
- public void run() {
- handlerIntent.sendEmptyMessage(0);
- }
- },
- 1000
- );
-
-
-
- }
-
- private Handler handlerIntent = new Handler ()
- {
+ setContentView(R.layout.chat_pager);
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
-
- if (msg.what == 0)
- resolveIntent(getIntent());
-
- }
-
- };
-
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
- private SlidingMenu menu = null;
-
- private void initSideBar ()
- {
- menu = new SlidingMenu(this);
- menu.setMode(SlidingMenu.LEFT);
- menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
- menu.setShadowWidthRes(R.dimen.shadow_width);
- menu.setShadowDrawable(R.drawable.shadow);
- menu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
- menu.setFadeDegree(0.35f);
- menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
-
- menu.setMenu(R.layout.fragment_drawer);
-
- Button btnDrawerAccount = (Button) findViewById(R.id.btnDrawerAccount);
- Button btnDrawerSettings = (Button) findViewById(R.id.btnDrawerSettings);
- Button btnDrawerPanic = (Button) findViewById(R.id.btnDrawerPanic);
- Button btnDrawerGroupChat = (Button) findViewById(R.id.btnDrawerGroupChat);
- Button btnDrawerAddContact = (Button) findViewById(R.id.btnDrawerAddContact);
-
-
- btnDrawerAccount.setOnClickListener(new OnClickListener ()
- {
+ mApp.setAppTheme(this,mToolbar);
+ ThemeableActivity.setBackgroundImage(this);
- @Override
- public void onClick(View v) {
-
- Intent intent = new Intent(NewChatActivity.this, AccountListActivity.class);
- startActivity(intent);
-
- }
-
-
- });
-
- btnDrawerSettings.setOnClickListener(new OnClickListener ()
- {
+ mToolbar.inflateMenu(R.menu.new_chat_menu);
+ setupMenu ();
- @Override
- public void onClick(View v) {
- Intent sintent = new Intent(NewChatActivity.this, SettingActivity.class);
- startActivity(sintent);
-
- }
-
- });
+ setTitle(R.string.app_name);
+
+ mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerToggle = new ActionBarDrawerToggle(
+ this, mDrawer, mToolbar,
+ R.string.ok, R.string.cancel
+ );
- btnDrawerPanic.setOnClickListener(new OnClickListener ()
+ // Set the drawer toggle as the DrawerListener
+ mDrawer.setDrawerListener(mDrawerToggle);
+ mDrawerToggle.setDrawerIndicatorEnabled(true);
+ mDrawerToggle.syncState();
+ mDrawerToggle.setToolbarNavigationClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
-
- /*
- Intent intent = new Intent(NewChatActivity.this, AccountListActivity.class);
- intent.putExtra("EXIT", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- */
-
-
- Uri packageURI = Uri.parse("package:info.guardianproject.otr.app.im");
- Intent intent = new Intent(Intent.ACTION_DELETE, packageURI);
- startActivity(intent);
-
-
+ int currentPos = mChatPager.getCurrentItem();
+ if (currentPos > 0) {
+ mChatPager.setCurrentItem(0);
+
+ }
+
}
-
+
+
});
+
+ mHandler = new MyHandler(this);
+ mRequestedChatId = -1;
+
+ ((Button)findViewById(R.id.btnAddAccount)).setOnClickListener (new OnClickListener ()
- btnDrawerGroupChat.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
-
- showGroupChatDialog();
-
-
+ // TODO Auto-generated method stub
+ startAccountSetup ();
}
});
-
- btnDrawerAddContact.setOnClickListener(new OnClickListener ()
- {
+ mChatPager = (ViewPager) findViewById(R.id.chatpager);
+ //mChatPager.setSaveEnabled(false);
+ //mChatPager.setOffscreenPageLimit(3);
+ //mChatPager.setDrawingCacheEnabled(true);
+ //mChatPager.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO);
+
+ mChatPager.setOnPageChangeListener(new SimpleOnPageChangeListener () {
@Override
- public void onClick(View v) {
-
- showInviteContactDialog();
-
-
- }
-
- });
-
-
-
-
- }
-
- private void showInviteContactDialog ()
- {
- if (mLastProviderId != -1 && mAccountId != -1)
- {
- Intent i = new Intent(this, AddContactActivity.class);
- i.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mLastProviderId);
- i.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
- // i.putExtra(ImServiceConstants.EXTRA_INTENT_LIST_NAME,
- // mContactListView.getSelectedContactList());
- startActivity(i);
- }
- }
+ public void onPageSelected(int pos) {
+ if (pos > 0) {
- @Override
- protected void onNewIntent(Intent intent) {
-
- new java.util.Timer().schedule(
- new java.util.TimerTask() {
- @Override
- public void run() {
- handlerIntent.sendEmptyMessage(0);
+ if (mLastPagePosition != -1)
+ {
+ ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(pos);
+ // Fragment isn't guaranteed to be initialized yet
+ if (frag != null)
+ frag.onDeselected(mApp);
}
- },
- 1000
- );
-
- }
- void resolveIntent(Intent intent) {
-
- if (requireOpenDashboardOnStart(intent)) {
- long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1L);
- mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
- -1L);
- if (providerId == -1L || mAccountId == -1L) {
- finish();
- } else {
- // mChatSwitcher.open();
- }
- return;
- }
+ ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(pos);
+ // Fragment isn't guaranteed to be initialized yet
+ if (frag != null)
+ frag.onSelected(mApp);
- if (ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION.equals(intent.getAction())) {
- long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1);
- mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
- -1L);
- String from = intent.getStringExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS);
- if ((providerId == -1) || (from == null)) {
- finish();
- } else {
- //chatView.bindSubscription(providerId, from);
-
- showSubscriptionDialog (providerId, from);
-
- }
- } else {
- Uri data = intent.getData();
-
- if (data != null)
- {
- if (data.getScheme().equals("immu"))
- {
- String user = data.getUserInfo();
- String host = data.getHost();
- String path = null;
-
- if (data.getPathSegments().size() > 0)
- path = data.getPathSegments().get(0);
-
- if (host != null && path != null)
+ mLastPagePosition = pos;
+
+ if (mMenu != null)
{
- List listConns = ((ImApp)getApplication()).getActiveConnections();
-
- if (!listConns.isEmpty())
- {
-
- startGroupChat(path, host, listConns.get(0));
-
-
- }
+
+ mMenu.setGroupVisible(R.id.menu_group_chats, true);
+ mMenu.setGroupVisible(R.id.menu_group_contacts, false);
+
}
+
+ mDrawerToggle.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
+ mDrawerToggle.setDrawerIndicatorEnabled(false);
+ mDrawerToggle.syncState();
+
+
}
else
{
- String type = getContentResolver().getType(data);
- if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) {
-
- long requestedChatId = ContentUris.parseId(data);
-
- if (mCursorChats != null)
- {
- mCursorChats.moveToPosition(0);
- int posIdx = 0;
- boolean foundChatView = false;
-
- while (mCursorChats.moveToNext())
- {
- long chatId = mCursorChats.getLong(ChatView.CHAT_ID_COLUMN);
-
- if (chatId == requestedChatId)
- {
- mChatPager.setCurrentItem(posIdx+1);
- foundChatView = true;
- break;
- }
-
- posIdx++;
- }
-
- if (!foundChatView)
- {
-
- Uri.Builder builder = Imps.Chats.CONTENT_URI.buildUpon();
- ContentUris.appendId(builder, requestedChatId);
- Cursor cursor = getContentResolver().query(builder.build(), ChatView.CHAT_PROJECTION, null, null, null);
-
- mContactList.startChat(cursor);
-
-
- }
- }
-
-
- } else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
- //chatView.bindInvitation(ContentUris.parseId(data));
-
-
+ if (mMenu != null)
+ {
+ mMenu.setGroupVisible(R.id.menu_group_chats, false);
+ mMenu.setGroupVisible(R.id.menu_group_contacts, true);
+
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
-
}
+
+ setTitle(R.string.app_name);
+ mDrawerToggle.setHomeAsUpIndicator(null);
+ mDrawerToggle.setDrawerIndicatorEnabled(true);
+ mDrawerToggle.syncState();
+
}
+
}
- else
- {
- mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,-1L);
-
- if (mContactList != null)
- mContactList.initAccount(this, mAccountId);
-
+
+
+ });
+
+ mMessageContextMenuHandler = new MessageContextMenuHandler();
+
+ // initSideBar ();
+
+ mChatPagerAdapter = new ChatViewPagerAdapter(getSupportFragmentManager());
+ mChatPager.setAdapter(mChatPagerAdapter);
+
+ if (icicle != null) {
+ if (icicle.containsKey(ICICLE_CHAT_PAGER_ADAPTER)) {
+ mChatPagerAdapter.restoreState(icicle.getParcelable(ICICLE_CHAT_PAGER_ADAPTER), getClassLoader());
}
- }
-
- if (mContactList != null)
- {
- mContactList.setSpinnerState(this);
- }
-
- }
-
- public void showChat (long requestedChatId)
- {
- mCursorChats.moveToPosition(-1);
- int posIdx = 1;
-
- while (mCursorChats.moveToNext())
- {
- long chatId = mCursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
-
- if (chatId == requestedChatId)
- {
- mChatPager.setCurrentItem(posIdx);
- break;
+ if (icicle.containsKey(ICICLE_POSITION)) {
+ int position = icicle.getInt(ICICLE_POSITION);
+ if (position < mChatPagerAdapter.getCount())
+ mChatPager.setCurrentItem(position);
}
-
- posIdx++;
}
- }
- public void refreshChatViews ()
- {
-
- mChatPagerAdapter = new ChatViewPagerAdapter(getSupportFragmentManager());
- mChatPager.setAdapter(mChatPagerAdapter);
-
-
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.chat_screen_menu, menu);
+ mApp.registerForBroadcastEvent(ImApp.EVENT_SERVICE_CONNECTED, mHandler);
- menuOtr = menu.findItem(R.id.menu_view_otr);
- menuCall = menu.findItem(R.id.menu_secure_call);
-
-
- return true;
- }
+ initConnections();
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
- //updateOtrMenuState();
- return true;
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
+ private void initChats ()
+ {
+ LoaderManager lMgr =getSupportLoaderManager();
+ lMgr.initLoader(CHAT_LIST_LOADER_ID, null, new LoaderManager.LoaderCallbacks () {
- case R.id.menu_secure_call:
- sendCallInvite ();
- return true;
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ CursorLoader loader = new CursorLoader(NewChatActivity.this, Imps.Contacts.CONTENT_URI_CHAT_CONTACTS, ChatView.CHAT_PROJECTION, null, null, null);
+ loader.setUpdateThrottle(100L);
- case R.id.menu_view_profile:
- //getChatView().viewProfile();
- return true;
+ return loader;
+ }
- case R.id.menu_end_conversation:
- //getChatView().closeChatSession();
- return true;
-
-
+ @Override
+ public void onLoadFinished(Loader loader, Cursor newCursor) {
+
+ mChatPagerAdapter.swapCursor(newCursor);
+ updateChatList();
+
+ if (getIntent() != null)
+ resolveIntent(getIntent());
+
+ if (mRequestedChatId >= 0) {
+ if (showChat(mRequestedChatId)) {
+ mRequestedChatId = -1;
+ }
+ }
- case android.R.id.home:
- finish();// close this view and return to account list
- return true;
-
- case R.id.menu_view_accounts:
- startActivity(new Intent(getBaseContext(), ChooseAccountActivity.class));
- // finish();
- return true;
-
-
- }
- return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ mChatPagerAdapter.swapCursor(null);
+ }
+ });
}
-
+
+
+
+ @Override
+ public void setTitle(CharSequence title) {
+
+ mToolbar.setTitle(title);
+ // mToolbar.setLogo(null);
+ }
+
+ public void setTitle(CharSequence title, Drawable icon) {
+
+ mToolbar.setTitle(title);
+ // mToolbar.setLogo(icon);
+
+ }
+
+
+ private void checkCustomFont ()
+ {
+ if (CustomTypefaceManager.getCurrentTypeface(this)==null)
+ {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ List mInputMethodProperties = imm.getEnabledInputMethodList();
+
+ final int N = mInputMethodProperties.size();
+
+ for (int i = 0; i < N; i++) {
+
+ InputMethodInfo imi = mInputMethodProperties.get(i);
+
+ //imi contains the information about the keyboard you are using
+ if (imi.getPackageName().equals("org.ironrabbit.bhoboard"))
+ {
+ CustomTypefaceManager.loadFromKeyboard(this);
+ break;
+ }
+
+ }
+
+
+ }
+ }
+
+ /*
+ * We must have been thawed and the service was not previously connected, so our ChatViews are showing nothing.
+ * Refresh them.
+ */
+ void onServiceConnected() {
+ if (mChatPagerAdapter != null) {
+ int size = mChatPagerAdapter.getCount();
+ for (int i = 1; i < size ; i++) {
+ ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(i);
+ if (frag != null) {
+ frag.onServiceConnected();
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ unregisterSubListeners ();
+
+ if (mGlobalSettings != null)
+ {
+ mGlobalSettings.close();
+ mGlobalSettings = null;
+ }
+
+ mApp.unregisterForBroadcastEvent(ImApp.EVENT_SERVICE_CONNECTED, mHandler);
+ mChatPagerAdapter.swapCursor(null);
+ //mAdapter.swapCursor(null);
+ super.onDestroy();
+ mChatPagerAdapter = null;
+ // mAdapter = null;
+
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(ICICLE_POSITION, mChatPager.getCurrentItem());
+ outState.putParcelable(ICICLE_CHAT_PAGER_ADAPTER, mChatPagerAdapter.saveState());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mApp.getTrustManager().bindDisplayActivity(this);
+
+ mApp.checkForCrashes(this);
+
+ //if VFS is not mounted, then send to WelcomeActivity
+ if (!VirtualFileSystem.get().isMounted())
+ {
+ finish();
+ startActivity(new Intent(this,WelcomeActivity.class));
+
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ mApp.getTrustManager().unbindDisplayActivity(this);
+
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ setIntent(intent);
+ resolveIntent(intent);
+ }
+
+ @Override
+ public void onBackPressed() {
+
+ int currentPos = mChatPager.getCurrentItem();
+ if (currentPos > 0) {
+ mChatPager.setCurrentItem(0);
+ return;
+ }
+
+ super.onBackPressed();
+ }
+
+ private void showInviteContactDialog ()
+ {
+
+ Intent i = new Intent(this, AddContactActivity.class);
+ startActivityForResult(i,REQUEST_ADD_CONTACT);
+
+ }
+
+ private IOtrChatSession getCurrentOtrChatSession() throws RemoteException {
+ IChatSession chatSession = getCurrentChatSession();
+ if (chatSession == null)
+ return null;
+ else
+ return chatSession.getOtrChatSession();
+ }
+
+ private void displayQRCode ()
+ {
+ try
+ {
+ IOtrChatSession iOtr = getCurrentOtrChatSession();
+ if (iOtr != null)
+ {
+ String localUser = iOtr.getLocalUserId();
+ String localFingerprint = iOtr.getLocalFingerprint();
+
+ if (localFingerprint != null)
+ {
+ String otrKeyURI = XmppUriHelper.getUri(localUser, localFingerprint);
+
+ new IntentIntegrator(this).shareText(otrKeyURI);
+ return;
+ }
+ }
+ }
+ catch (RemoteException re)
+ {
+ }
+
+ //did not work
+ Toast.makeText(this, R.string.please_start_a_secure_conversation_before_scanning_codes, Toast.LENGTH_LONG).show();
+ }
+
+ private void resolveIntent(Intent intent) {
+
+ doResolveIntent(intent);
+ setIntent(null);
+
+ }
+
+ private IImConnection findConnectionForGroupChat (String user, String host)
+ {
+ Collection connActive = mApp.getActiveConnections();
+ ContentResolver cr = this.getContentResolver();
+ IImConnection result = null;
+
+ for (IImConnection conn : connActive)
+ {
+ try
+ {
+
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( conn.getProviderId())},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, cr,
+ conn.getProviderId(), false /* keep updated */, mHandler /* no handler */);
+
+ if (host.contains(settings.getDomain()))
+ {
+ if (conn.getState() == ImConnection.LOGGED_IN)
+ {
+
+ result = conn;
+ settings.close();
+ pCursor.close();
+ break;
+ }
+ }
+
+ settings.close();
+ pCursor.close();
+
+ }
+ catch (RemoteException e){//nothing to do here
+ }
+
+ }
+
+ return result;
+ }
+
+ private void doResolveIntent(Intent intent) {
+
+ if (requireOpenDashboardOnStart(intent)) {
+ long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1L);
+ mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
+ -1L);
+ if (providerId == -1L || mLastAccountId == -1L) {
+ finish();
+ } else {
+ // mChatSwitcher.open();
+ }
+ return;
+ }
+
+ if (ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION.equals(intent.getAction())) {
+
+ long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1);
+ mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
+ -1L);
+ String from = intent.getStringExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS);
+
+ if ((providerId == -1) || (from == null)) {
+ finish();
+ } else {
+
+ showSubscriptionDialog (providerId, from);
+
+ }
+ } else if (intent != null) {
+ Uri data = intent.getData();
+
+ if (intent.getBooleanExtra("showaccounts", false))
+ mDrawer.openDrawer(GravityCompat.START);
+
+ if (data != null)
+ {
+ if (data.getScheme() != null && data.getScheme().equals("immu"))
+ {
+ String user = data.getUserInfo();
+ String host = data.getHost();
+ String path = null;
+
+ if (data.getPathSegments().size() > 0)
+ path = data.getPathSegments().get(0);
+
+ if (host != null && path != null)
+ {
+
+ IImConnection connMUC = findConnectionForGroupChat(user, host);
+
+ if (connMUC != null)
+ {
+
+ startGroupChat (path, host, user, connMUC);
+ setResult(RESULT_OK);
+ }
+ else
+ {
+ mHandler.showAlert("Connection Error", "Unable to find a connection to join a group chat from. Please sign in and try again.");
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+
+ }
+
+
+
+ } else {
+
+ String type = getContentResolver().getType(data);
+ if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) {
+
+ long requestedContactId = ContentUris.parseId(data);
+
+ Cursor cursorChats = mChatPagerAdapter.getCursor();
+
+ if (cursorChats != null)
+ {
+ cursorChats.moveToPosition(-1);
+ int posIdx = 1;
+ boolean foundChatView = false;
+
+ while (cursorChats.moveToNext())
+ {
+ long chatId = cursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
+
+ if (chatId == requestedContactId)
+ {
+ mChatPager.setCurrentItem(posIdx);
+ foundChatView = true;
+ break;
+ }
+
+ posIdx++;
+ }
+
+ if (!foundChatView)
+ {
+
+ Uri.Builder builder = Imps.Contacts.CONTENT_URI.buildUpon();
+ ContentUris.appendId(builder, requestedContactId);
+ Cursor cursor = getContentResolver().query(builder.build(), ChatView.CHAT_PROJECTION, null, null, null);
+
+ try {
+ if (cursor.getCount() > 0)
+ {
+ cursor.moveToFirst();
+ openExistingChat(cursor);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ } else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
+ //chatView.bindInvitation(ContentUris.parseId(data));
+
+
+
+
+ }
+ }
+ }
+ else if (intent.hasExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID))
+ {
+ //set the current account id
+ mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,-1L);
+
+ //move the pager back to the first page
+ if (mChatPager != null)
+ mChatPager.setCurrentItem(0);
+
+
+
+ }
+ else
+ {
+ // refreshConnections();
+ }
+ }
+
+
+ }
+
+ public boolean showChat (long requestedChatId)
+ {
+ Cursor cursorChats = mChatPagerAdapter.getCursor();
+
+ if (cursorChats == null)
+ return false;
+
+ cursorChats.moveToPosition(-1);
+ int posIdx = 1;
+
+ while (cursorChats.moveToNext())
+ {
+ long chatId = cursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
+
+ if (chatId == requestedChatId)
+ {
+ mChatPager.setCurrentItem(posIdx);
+ return true;
+ }
+
+ posIdx++;
+ }
+
+ // Was not found
+ return false;
+ }
+
+ public void refreshChatViews ()
+ {
+ mChatPagerAdapter.notifyDataSetChanged();
+
+ }
+
+ private Menu mMenu;
+ // private AccountAdapter mAdapter;
+ protected Long[][] mAccountIds;
+ private long mRequestedChatId;
+
+ public void updateEncryptionMenuState ()
+ {
+ ChatView cView = getCurrentChatView();
+
+ if (cView == null)
+ return;
+
+ if (mChatPager != null && mMenu != null)
+ {
+ if (mChatPager.getCurrentItem() > 0)
+ {
+ // phoenix-nz - a group chat should not be shown as 'unverified' as it (currently)
+ // cannot be verified. Thus, show as neutral.
+ if (cView.isGroupChat())
+ {
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
+
+ }
+ else if (cView.getOtrSessionStatus() == SessionStatus.ENCRYPTED && cView.isOtrSessionVerified())
+ {
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,true);
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
+
+ }
+ else if (cView.getOtrSessionStatus() == SessionStatus.ENCRYPTED)
+ {
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,true);
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
+
+
+
+ }
+ else if (cView.getOtrSessionStatus() == SessionStatus.FINISHED)
+ {
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,true);
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
+
+ }
+ else
+ {
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,true);
+
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false);
+
+ }
+
+ }
+
+ }
+ }
+
+ private void setupMenu ()
+ {
+
+ mMenu = mToolbar.getMenu();
+
+ if (mMenu != null)
+ {
+ if (mChatPager != null && mChatPager.getCurrentItem() > 0)
+ {
+ mMenu.setGroupVisible(R.id.menu_group_chats, true);
+ mMenu.setGroupVisible(R.id.menu_group_contacts, false);
+
+ }
+ else
+ {
+ mMenu.setGroupVisible(R.id.menu_group_chats, false);
+ mMenu.setGroupVisible(R.id.menu_group_contacts, true);
+
+ mMenu.setGroupVisible(R.id.menu_group_otr_verified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false);
+ mMenu.setGroupVisible(R.id.menu_group_otr_off,false);
+
+ }
+ }
+
+ mToolbar.setOnMenuItemClickListener(new OnMenuItemClickListener ()
+ {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+
+
+ switch (item.getItemId()) {
+/*
+ case R.id.menu_send_image:
+ if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED)
+ {
+ startImagePicker();
+ }
+ else
+ {
+ mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files));
+ }
+ return true;
+ case R.id.menu_take_picture:
+ if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED)
+ {
+ startPhotoTaker();
+ }
+ else
+ {
+ mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files));
+ }
+ return true;
+
+ case R.id.menu_send_file:
+
+ if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED)
+ {
+ startFilePicker();
+ }
+ else
+ {
+ mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files));
+ }
+
+ return true;
+
+ case R.id.menu_send_audio:
+
+ if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED)
+ {
+ startAudioPicker();
+ }
+ else
+ {
+ mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files));
+ }
+
+ return true;
+*/
+ case R.id.menu_verify_or_view:
+ case R.id.menu_view_profile_verified:
+
+ if (getCurrentChatView() != null)
+ getCurrentChatView().showVerifyDialog();
+ return true;
+
+ case R.id.menu_show_qr:
+ displayQRCode();
+ return true;
+ case R.id.menu_end_conversation:
+ try {
+ endCurrentChatPrompt( getCurrentSessionId());
+ } catch (Exception e) {
+ Toast.makeText(NewChatActivity.this, "Error:" + e.getMessage(), Toast.LENGTH_LONG).show(); // TODO i18n
+ e.printStackTrace();
+ }
+
+ return true;
+ /*
+ case R.id.menu_delete_conversation:
+ if (getCurrentChatView() != null)
+ getCurrentChatView().closeChatSession(true);
+ return true;
+ */
+ case R.id.menu_settings:
+ Intent sintent = new Intent(NewChatActivity.this, SettingActivity.class);
+ startActivityForResult(sintent,REQUEST_SETTINGS);
+ return true;
+
+ case R.id.menu_otr:
+ case R.id.menu_otr_stop:
+ case R.id.menu_otr_stop_verified:
+
+ if (getCurrentChatView() != null)
+ {
+
+ boolean isEnc = (getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED ||
+ getCurrentChatView().getOtrSessionStatus() == SessionStatus.FINISHED
+ );
+
+ getCurrentChatView().setOTRState(!isEnc);
+
+
+
+ }
+
+ return true;
+
+ case android.R.id.home:
+ int currentPos = mChatPager.getCurrentItem();
+ if (currentPos > 0) {
+ mChatPager.setCurrentItem(0);
+
+ }
+
+ return true;
+
+ case R.id.menu_view_accounts:
+ startAccountSetup();
+
+ return true;
+
+ case R.id.menu_new_chat:
+ startContactPicker();
+ return true;
+
+ case R.id.menu_exit:
+ WelcomeActivity.shutdownAndLock(NewChatActivity.this);
+ ExitActivity.exitAndRemoveFromRecentApps(NewChatActivity.this);
+ return true;
+
+ case R.id.menu_add_contact:
+ showInviteContactDialog();
+ return true;
+
+ case R.id.menu_group_chat:
+ showGroupChatDialog();
+ return true;
+ case R.id.menu_import_keys:
+ importKeyStore();
+ return true;
+ }
+
+ return false;
+
+ }
+
+ });
+
+
+ }
+
+ private void startAccountSetup ()
+ {
+ startActivity(new Intent(getBaseContext(), ChooseAccountActivity.class));
+
+ }
+
+ private void importKeyStore ()
+ {
+ OtrAndroidKeyManagerImpl.checkForKeyImport(getIntent(), this);
+ }
+
+ private void endCurrentChatPrompt( final String sessionId ) {
+ OtrChatManager otrChatManager = OtrChatManager.getInstance();
+ if (otrChatManager != null) {
+ try {
+ IOtrChatSession otrSession = getCurrentOtrChatSession();
+ otrSession.stopChatEncryption();
+ } catch (RemoteException e) {
+ }
+ }
+ // if no files to delete - just end the session
+ if (!ChatFileStore.sessionExists(sessionId)) {
+ endCurrentChat();
+ return;
+ }
+ new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(getString(R.string.end_chat_title))
+ .setMessage(getString(R.string.end_chat_summary))
+ .setPositiveButton(getString(R.string.end_chat_and_delete), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ endCurrentChat();
+ }
+ })
+ .setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ return;
+ }
+ })
+ .show();
+ }
+
+ private void endCurrentChat()
+ {
+ if (getCurrentChatView() != null) {
+ try {
+ // delete the chat session's files if any
+ deleteSessionVfs( getCurrentSessionId() );
+ } catch (Exception e) {
+ // TODO error
+ e.printStackTrace();
+ }
+ getCurrentChatView().closeChatSession(true);
+ }
+
+ updateChatList();
+ mChatPager.setCurrentItem(0);
+
+ }
+
+ private void deleteSessionVfs( final String sessionId ) throws Exception {
+ // if no files to delete - bail
+ if (!ChatFileStore.sessionExists(sessionId)) {
+ return;
+ }
+ // delete
+ ChatFileStore.deleteSession(sessionId);
+ }
+
+ private void startContactPicker() {
+
+ Uri.Builder builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
+ Uri data = builder.build();
+
+ Intent i = new Intent(Intent.ACTION_PICK, data);
+ i.putExtra("invitations", false);
+ startActivityForResult(i, REQUEST_PICK_CONTACTS);
+ }
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
@@ -498,7 +1099,7 @@ public boolean dispatchKeyEvent(KeyEvent event) {
&& event.getAction() == KeyEvent.ACTION_DOWN) {
mChatView.closeChatSessionIfInactive();
}*/
-
+
return super.dispatchKeyEvent(event);
}
@@ -507,171 +1108,328 @@ private boolean requireOpenDashboardOnStart(Intent intent) {
return intent.getBooleanExtra(ImServiceConstants.EXTRA_INTENT_SHOW_MULTIPLE, false);
}
- private void sendCallInvite ()
- {
-
- // getChatView().sendMessage("☎ Click to start call sip:" + this.mSipAccount + "");
-
+ void startImagePicker() {
+ Intent intent = new Intent(Intent.ACTION_PICK);
+ intent.setType("image/*");
+ startActivityForResult(intent, REQUEST_SEND_IMAGE);
}
-
- public void setOTRState(ChatView chatView, IOtrChatSession otrChatSession, boolean otrEnabled) {
-
- if (otrChatSession != null)
- {
- try {
- // SessionStatus sessionStatus = SessionStatus.values()[otrChatSession.getChatStatus()];
-
- if (otrEnabled) {
- otrChatSession.startChatEncryption();
- }
- else
- {
- otrChatSession.stopChatEncryption();
- }
-
-
- } catch (RemoteException e) {
- Log.d("Gibber", "error getting remote activity", e);
- }
-
- chatView.updateWarningView();
- }
+ Uri mLastPhoto = null;
+
+ void startPhotoTaker() {
+
+ // create Intent to take a picture and return control to the calling application
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ File photo = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "cs_" + new Date().getTime() + ".jpg");
+ mLastPhoto = Uri.fromFile(photo);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT,
+ mLastPhoto);
+
+ // start the image capture Intent
+ startActivityForResult(intent, REQUEST_TAKE_PICTURE);
}
- /*
- public void updateOtrMenuState() {
-
- ChatView chatView = getCurrentChatView ();
+ void startPhotoTakerSecure() {
- if (menuOtr == null || chatView == null)
- return;
+ // create Intent to take a picture and return control to the calling application
+ Intent intent = new Intent(this, SecureCameraActivity.class);
+ String time = ""+new Date().getTime();
+ String filename = "/" + Environment.DIRECTORY_DCIM + "/" + "cs_" + time + ".jpg";
+ String thumbnail = "/" + Environment.DIRECTORY_DCIM + "/" + "cs_" + time + "_thumb.jpg";
+ intent.putExtra(SecureCameraActivity.FILENAME, filename ) ;
+ intent.putExtra(SecureCameraActivity.THUMBNAIL, thumbnail ) ;
+
+ // start the secure image capture Intent
+ startActivityForResult(intent, REQUEST_TAKE_PICTURE_SECURE);
+ }
+
+ void startFilePicker() {
+ Intent selectFile = new Intent(Intent.ACTION_GET_CONTENT);
+ selectFile.setType("file/*");
+ Intent intentChooser = Intent.createChooser(selectFile, "Select File");
+
+ if (intentChooser != null)
+ startActivityForResult(Intent.createChooser(selectFile, "Select File"), REQUEST_SEND_FILE);
+ }
+
+ void startAudioPicker() {
+
+
+ Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+ if (!isCallable(intent))
+ {
+ intent = new Intent("android.provider.MediaStore.RECORD_SOUND");
+ intent.addCategory("android.intent.category.DEFAULT");
+
+ if (!isCallable(intent))
+ {
+ intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("audio/*");
+
+ if (!isCallable(intent))
+ return;
+
+ }
+ }
- IOtrChatSession otrChatSession = chatView.getOtrChatSession();
+ startActivityForResult(intent, REQUEST_SEND_AUDIO); // intent and requestCode of 1
- if (otrChatSession != null) {
- try {
- SessionStatus sessionStatus = SessionStatus.values()[otrChatSession.getChatStatus()];
+ }
- if (sessionStatus != SessionStatus.PLAINTEXT) {
- menuOtr.setTitle(R.string.menu_otr_stop);
- menuOtr.setIcon(this.getResources().getDrawable(R.drawable.ic_menu_encrypt));
-
- } else {
- menuOtr.setTitle(R.string.menu_otr_start);
- menuOtr.setIcon(this.getResources().getDrawable(R.drawable.ic_menu_unencrypt));
+ private boolean isCallable(Intent intent) {
+ List list = getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+ private void handleSendDelete( Uri contentUri, boolean delete, boolean resizeImage) {
+ try {
+ // import
+ FileInfo info = SystemServices.getFileInfoFromURI(this, contentUri);
+ String sessionId = getCurrentSessionId();
+ Uri vfsUri;
+ if (resizeImage)
+ vfsUri = ChatFileStore.resizeAndImportImage(this, sessionId, contentUri, info.type);
+ else
+ vfsUri = ChatFileStore.importContent(sessionId, info.path);
+ // send
+ boolean sent = handleSend(vfsUri, info.type);
+ if (!sent) {
+ // not deleting if not sent
+ return;
+ }
+ // autu delete
+ if (delete) {
+ boolean deleted = delete(contentUri);
+ if (!deleted) {
+ throw new IOException("Error deleting " + contentUri);
}
-
- } catch (RemoteException e) {
- Log.d("NewChat", "Error accessing remote service", e);
}
- } else {
- menuOtr.setTitle(R.string.menu_otr_start);
+ } catch (Exception e) {
+ // Toast.makeText(this, "Error sending file", Toast.LENGTH_LONG).show(); // TODO i18n
+ Log.e(ImApp.LOG_TAG,"error sending file",e);
+ }
+ }
+ private boolean delete(Uri uri) {
+ if (uri.getScheme().equals("content")) {
+ int deleted = getContentResolver().delete(uri,null,null);
+ return deleted == 1;
+ }
+ if (uri.getScheme().equals("file")) {
+ java.io.File file = new java.io.File(uri.toString().substring(5));
+ return file.delete();
}
- }*/
+ return false;
+ }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) {
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_SEND_IMAGE || requestCode == REQUEST_SEND_FILE || requestCode == REQUEST_SEND_AUDIO) {
+ Uri uri = resultIntent.getData() ;
+ if( uri == null ) {
+ return ;
+ }
+ boolean deleteAudioFile = (requestCode == REQUEST_SEND_AUDIO);
+ boolean resizeImage = requestCode == REQUEST_SEND_IMAGE; //resize if is an image, not shared as "file"
+ handleSendDelete(uri, deleteAudioFile, resizeImage);
+ }
+ else if (requestCode == REQUEST_TAKE_PICTURE)
+ {
+ File file = new File(getRealPathFromURI(mLastPhoto));
+ final Handler handler = new Handler();
+ MediaScannerConnection.scanFile(
+ this, new String[] { file.toString() }, null,
+ new MediaScannerConnection.OnScanCompletedListener() {
+ @Override
+ public void onScanCompleted(String path, final Uri uri) {
+
+ handler.post( new Runnable() {
+ @Override
+ public void run() {
+ handleSendDelete(mLastPhoto, true, true);
+ }
+ });
+ }
+ });
+ }
+ else if (requestCode == REQUEST_TAKE_PICTURE_SECURE)
+ {
+ String filename = resultIntent.getStringExtra(SecureCameraActivity.FILENAME);
+ String mimeType = resultIntent.getStringExtra(SecureCameraActivity.MIMETYPE);
+ Uri uri = Uri.parse("file:" + filename);
+ handleSend(uri,mimeType);
+ }
+ else if (requestCode == REQUEST_SETTINGS)
+ {
- /*
- private void switchChat(int delta) {
-
- ChatView chatView = getCurrentChatView ();
- long providerId = chatView.getProviderId();
- long accountId = chatView.getAccountId();
- String contact = chatView.getUserName();
+ try {
+ mApp.getRemoteImService().updateStateFromSettings();
+ } catch (Exception e) {
+ Log.e(ImApp.LOG_TAG,"unable to update service settings",e);
+ }
- mChatSwitcher.rotateChat(delta, contact, accountId, providerId);
- }*/
+ finish();
+ Intent intent = new Intent(getApplicationContext(), NewChatActivity.class);
+ startActivity(intent);
- /*
- private void startContactPicker() {
- Uri.Builder builder = Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
- ContentUris.appendId(builder, getChatView().getProviderId());
- ContentUris.appendId(builder, getChatView().getAccountId());
- Uri data = builder.build();
+ }
+ else if (requestCode == REQUEST_PICK_CONTACTS || requestCode == REQUEST_ADD_CONTACT) {
- try {
- Intent i = new Intent(Intent.ACTION_PICK, data);
- i.putExtra(ContactsPickerActivity.EXTRA_EXCLUDED_CONTACTS, getChatView()
- .getCurrentChatSession().getParticipants());
- startActivityForResult(i, REQUEST_PICK_CONTACTS);
- } catch (RemoteException e) {
- mHandler.showServiceErrorAlert();
- }
- }*/
+ String username = resultIntent.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME);
+ long providerId = resultIntent.getLongExtra(ContactsPickerActivity.EXTRA_RESULT_PROVIDER,-1);
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-
- /*
- if (resultCode == RESULT_OK) {
- if (requestCode == REQUEST_PICK_CONTACTS) {
- String username = data.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME);
+ //String message = resultIntent.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_MESSAGE);
try {
- IChatSession chatSession = getChatView().getCurrentChatSession();
- if (chatSession.isGroupChatSession()) {
+
+ IChatSession chatSession = this.getCurrentChatSession();
+ if (chatSession != null && chatSession.isGroupChatSession()) {
chatSession.inviteContact(username);
showInvitationHasSent(username);
} else {
- chatSession.convertToGroupChat();
- new ContactInvitor(chatSession, username).start();
+ startChat(providerId, username,Imps.ContactsColumns.TYPE_NORMAL,true, null);
}
} catch (RemoteException e) {
- mHandler.showServiceErrorAlert();
+ mHandler.showServiceErrorAlert("Error picking contacts");
+ Log.d(ImApp.LOG_TAG,"error picking contact",e);
+ }
+ }
+
+ IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode,
+ resultIntent);
+
+ if (scanResult != null) {
+ String scannedString = scanResult.getContents();
+ if (scannedString.startsWith("xmpp")) {
+ String result = XmppUriHelper.getOtrFingerprint(scannedString);
+ if (getCurrentChatView()!=null && result != null)
+ getCurrentChatView().verifyScannedFingerprint(result);
+ } else {
+ OtrAndroidKeyManagerImpl.handleKeyScanResult(scannedString, this);
}
}
- }*/
+ }
}
- void showInvitationHasSent(String contact) {
- Toast.makeText(NewChatActivity.this, getString(R.string.invitation_sent_prompt, contact),
- Toast.LENGTH_SHORT).show();
- }
+ IChatSession getCurrentChatSession() {
+ int currentPos = mChatPager.getCurrentItem();
+ if (currentPos == 0)
+ return null;
+ Cursor cursorChats = mChatPagerAdapter.getCursor();
+ cursorChats.moveToPosition(currentPos - 1);
+ long providerId = cursorChats.getLong(ChatView.PROVIDER_COLUMN);
+ String username = cursorChats.getString(ChatView.USERNAME_COLUMN);
+ IChatSessionManager sessionMgr = getChatSessionManager(providerId);
+ if (sessionMgr != null) {
+ try {
+ IChatSession session = sessionMgr.getChatSession(username);
- private class ContactInvitor extends ChatListenerAdapter {
- private final IChatSession mChatSession;
- String mContact;
+ if (session == null)
+ session = sessionMgr.createChatSession(username, false);
- public ContactInvitor(IChatSession session, String data) {
- mChatSession = session;
- mContact = data;
+ return session;
+ } catch (RemoteException e) {
+
+ mHandler.showServiceErrorAlert(e.getLocalizedMessage());
+ LogCleaner.error(ImApp.LOG_TAG, "send message error",e);
+ }
}
- @Override
- public void onConvertedToGroupChat(IChatSession ses) {
- try {
- final long chatId = mChatSession.getId();
- mChatSession.inviteContact(mContact);
- mHandler.post(new Runnable() {
- public void run() {
-
- ChatView chatView = getCurrentChatView ();
+ return null;
+ }
- if (chatView != null)
- {
- chatView.bindChat(chatId);
- showInvitationHasSent(mContact);
- }
- }
- });
- mChatSession.unregisterChatListener(this);
- } catch (RemoteException e) {
+ private String getCurrentSessionId() throws Exception {
+ return ""+getCurrentChatSession().getId();
+ }
+
+ private IChatSessionManager getChatSessionManager(long providerId) {
+ IImConnection conn = mApp.getConnection(providerId);
+ if (conn != null) {
+ try {
+ return conn.getChatSessionManager();
+ } catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
- LogCleaner.error(ImApp.LOG_TAG, "group chat error",e);
+ LogCleaner.error(ImApp.LOG_TAG, "send message error",e);
}
}
+ return null;
+ }
+
+
+ //----------------------------------------
+ /**
+ * This method is used to get real path of file from from uri
+ *
+ * @param contentUri
+ * @return String
+ */
+ //----------------------------------------
+ public String getRealPathFromURI(Uri contentUri)
+ {
+ try
+ {
+ String[] proj = {MediaStore.Images.Media.DATA};
+ Cursor cursor = managedQuery(contentUri, proj, null, null, null);
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ cursor.moveToFirst();
+ return cursor.getString(column_index);
+ }
+ catch (Exception e)
+ {
+ return contentUri.getPath();
+ }
+ }
+
+ private boolean handleSend(Uri uri, String mimeType) {
+ try {
+ FileInfo info = SystemServices.getFileInfoFromURI(this, uri);
- public void start() throws RemoteException {
- mChatSession.registerChatListener(this);
+ if (mimeType != null)
+ info.type = mimeType;
+
+ if (info != null && info.path != null && ChatFileStore.exists(info.path))
+ {
+ IChatSession session = getCurrentChatSession();
+
+ if (session != null) {
+ if (info.type == null)
+ if (mimeType != null)
+ info.type = mimeType;
+ else
+ info.type = "application/octet-stream";
+
+ String offerId = UUID.randomUUID().toString();
+ session.offerData(offerId, info.path, info.type );
+ ChatView cView = getCurrentChatView();
+ int type = cView.isOtrSessionVerified() ? Imps.MessageType.OUTGOING_ENCRYPTED_VERIFIED : Imps.MessageType.OUTGOING_ENCRYPTED;
+ Imps.insertMessageInDb(
+ getContentResolver(), false, session.getId(), true, null, uri.toString(),
+ System.currentTimeMillis(), type,
+ 0, offerId, info.type);
+ return true; // sent
+ }
+ }
+ else
+ {
+ Toast.makeText(this, R.string.sorry_we_cannot_share_that_file_type, Toast.LENGTH_LONG).show();
+ }
+ } catch (RemoteException e) {
+ Log.e(ImApp.LOG_TAG,"error sending file",e);
}
+ return false; // was not sent
+ }
+
+ void showInvitationHasSent(String contact) {
+ Toast.makeText(NewChatActivity.this, getString(R.string.invitation_sent_prompt, contact),
+ Toast.LENGTH_SHORT).show();
}
/** Show the context menu on a history item. */
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-
+
ChatView chatView = getCurrentChatView ();
if (chatView != null)
@@ -681,28 +1439,33 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn
Cursor cursor = chatView.getMessageAtPosition(info.position);
int type = cursor.getInt(cursor.getColumnIndexOrThrow(Imps.Messages.TYPE));
if (type == Imps.MessageType.OUTGOING) {
- menu.add(0, MENU_RESEND, 0, R.string.menu_resend).setOnMenuItemClickListener(
+ android.view.MenuItem mi = menu.add(0, MENU_RESEND, 0, R.string.menu_resend);
+
+ mi.setOnMenuItemClickListener(
mMessageContextMenuHandler);
+
+
+
}
-
-
+
+
}
}
- final class MessageContextMenuHandler implements OnMenuItemClickListener {
+ final class MessageContextMenuHandler implements android.view.MenuItem.OnMenuItemClickListener {
int mPosition;
-
+
@Override
public boolean onMenuItemClick(android.view.MenuItem item) {
-
+
ChatView chatView = getCurrentChatView ();
-
+
if (chatView != null)
{
Cursor c;
c = chatView.getMessageAtPosition(mPosition);
-
+
switch (item.getItemId()) {
case MENU_RESEND:
String text = c.getString(c.getColumnIndexOrThrow(Imps.Messages.BODY));
@@ -716,163 +1479,338 @@ public boolean onMenuItemClick(android.view.MenuItem item) {
return false;
}
}
-
-
- public class ChatViewPagerAdapter extends FragmentStatePagerAdapter {
-
-
+ public class ChatViewPagerAdapter extends DynamicPagerAdapter {
+ Cursor mCursor;
+ boolean mDataValid;
+
public ChatViewPagerAdapter(FragmentManager fm) {
super(fm);
-
- // if (mCursorChats != null && (!mCursorChats.isClosed()))
- // mCursorChats.close();
-
- if (mCursorChats == null)
- mCursorChats = getContentResolver().query(Imps.Contacts.CONTENT_URI_CHAT_CONTACTS, ChatView.CHAT_PROJECTION, null, null, null);
- else
- mCursorChats.requery();
}
-
-
-
- @Override
- public void notifyDataSetChanged() {
-
- mCursorChats.requery();
-
- super.notifyDataSetChanged();
+ public Cursor getCursor() {
+ return mCursor;
+ }
- for (int i = 1; i < getCount(); i++)
- {
- ChatViewFragment frag = ((ChatViewFragment)getItem(i));
- View fragView = frag.getView();
-
- if (frag != null && fragView != null && fragView instanceof ChatView)
- {
- ((ChatView)fragView).rebind();
- fragView.invalidate();
- }
+ public Cursor swapCursor(Cursor newCursor) {
+ if (newCursor == mCursor) {
+ return null;
}
+ Cursor oldCursor = mCursor;
+ mCursor = newCursor;
+ if (newCursor != null) {
+ mDataValid = true;
+
+
+ } else {
+ mDataValid = false;
+ }
+ notifyDataSetChanged();
+ // notify the observers about the new cursor
+ refreshChatViews();
-
+ return oldCursor;
}
-
@Override
public int getCount() {
- if (mCursorChats != null && (!mCursorChats.isClosed()))
- return mCursorChats.getCount() + 1;
+ if (mCursor != null)
+ return mCursor.getCount() + 1;
else
- return 1;
+ return 0;
}
@Override
public Fragment getItem(int position) {
-
if (position == 0)
{
- return (mContactList = new ContactListFragment());
+ if (mContactList == null)
+ mContactList = new ContactListFragment();
+
+
+ return mContactList;
}
else
{
int positionMod = position - 1;
+
+ mCursor.moveToPosition(positionMod);
+ long contactChatId = mCursor.getLong(ChatView.CONTACT_ID_COLUMN);
+ String contactName = mCursor.getString(ChatView.USERNAME_COLUMN);
+ long providerId = mCursor.getLong(ChatView.PROVIDER_COLUMN);
- long contactChatId = -1;
-
- mCursorChats.moveToPosition(positionMod);
- contactChatId = mCursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
+ int chatType = mCursor.getInt(ChatView.TYPE_COLUMN);
- return ChatViewFragment.newInstance(contactChatId);
+
+ return ChatViewFragment.newInstance(contactChatId, contactName, providerId);
}
}
@Override
public int getItemPosition(Object object) {
-
+
if (object instanceof ChatViewFragment)
{
- ChatViewFragment cvFrag = (ChatViewFragment)object;
- int position = -1;
-
- mCursorChats.moveToFirst();
-
- int posIdx = 1;
-
- do {
- long chatId = mCursorChats.getLong(ChatView.CHAT_ID_COLUMN);
-
- View view = cvFrag.getView();
-
- if (view instanceof ChatView && chatId == ((ChatView)view).mLastChatId)
- {
- position = posIdx;
-
- break;
+ ChatViewFragment cvFrag = (ChatViewFragment)object;
+ ChatView view = cvFrag.getChatView();
+ long viewChatId = view.mLastChatId;
+ int position = PagerAdapter.POSITION_NONE;
+
+ // TODO: cache positions so we don't scan the cursor every time
+ if (mCursor != null && mCursor.getCount() > 0)
+ {
+ mCursor.moveToFirst();
+
+ int posIdx = 1;
+
+ do {
+ long chatId = mCursor.getLong(ChatView.CHAT_ID_COLUMN);
+
+ if (chatId == viewChatId)
+ {
+ position = posIdx;
+ break;
+ }
+
+ posIdx++;
+ }
+ while (mCursor.moveToNext());
+
+ }
+
+ //` Log.d(TAG, "position of " + cvFrag.getArguments().getString("contactName") + " = " + position);
+ return position;
+
+ }
+ else if (object instanceof ContactListFragment)
+ {
+ return 0;
+
+ }
+ else {
+ throw new RuntimeException("got asked about an unknown fragment");
+ }
+ }
+
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+
+ if (position == 0 || mCursor == null)
+ {
+ if (mShowChatsOnly)
+ return getString(R.string.title_chats);
+ else
+ return getString(R.string.contacts);
+ }
+ else
+ {
+ int positionMod = position - 1;
+
+ mCursor.moveToPosition(positionMod);
+ if (!mCursor.isAfterLast())
+ {
+
+
+ String nickname = mCursor.getString(ChatView.NICKNAME_COLUMN);
+ int presence = mCursor.getInt(ChatView.PRESENCE_STATUS_COLUMN);
+ int type = mCursor.getInt(ChatView.TYPE_COLUMN);
+
+ BrandingResources brandingRes = mApp.getBrandingResource(mCursor.getInt(ChatView.PROVIDER_COLUMN));
+
+
+ SpannableString s = null;
+
+ Drawable statusIcon = null;
+
+ if (Imps.Contacts.TYPE_GROUP == type)
+ {
+ s = new SpannableString(nickname);
+ }
+ else
+ {
+ s = new SpannableString("+ " + nickname);
+ statusIcon = brandingRes.getDrawable(PresenceUtils.getStatusIconId(presence));
+ statusIcon.setBounds(0, 0, statusIcon.getIntrinsicWidth(),
+ statusIcon.getIntrinsicHeight());
+ s.setSpan(new ImageSpan(statusIcon), 0, 1, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ }
+
+
+ return s;
+
+ }
+ else
+ return "";//unknown title
+ }
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int pos) {
+ Object item = super.instantiateItem(container, pos);
+ return item;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int pos, Object object) {
+
+ super.destroyItem(container, pos, object);
+ }
+
+ public ChatView getChatViewAt(int pos) {
+ if (pos > 0)
+ {
+ ChatViewFragment frag = ((ChatViewFragment)getItemAt(pos));
+
+ if (frag != null)
+ return frag.getChatView();
+ }
+
+ return null; //this can happen if the user is quickly closing chats; just return null and swallow the event
+ //throw new RuntimeException("could not get chat view at " + pos);
+ }
+ }
+
+
+ private void initConnections ()
+ {
+ getSupportLoaderManager().initLoader(CHAT_PAGE_LOADER_ID, null, new LoaderCallbacks() {
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ CursorLoader loader = new CursorLoader(NewChatActivity.this, Imps.Provider.CONTENT_URI_WITH_ACCOUNT, ContactListFragment.PROVIDER_PROJECTION,
+ Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL",
+
+ new String[] { ImApp.IMPS_CATEGORY } ,
+ Imps.Provider.DEFAULT_SORT_ORDER);
+ loader.setUpdateThrottle(50L);
+ return loader;
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor newCursor) {
+
+ if (newCursor != null && newCursor.getCount() > 0)
+ {
+ mAccountIds = new Long[newCursor.getCount()][2];
+ newCursor.moveToFirst();
+ int activeAccountIdColumn = 4;
+ int activeProviderIdColumn = 0;
+
+ for (int i = 0; i < mAccountIds.length; i++)
+ {
+ mAccountIds[i][0] = newCursor.getLong(activeAccountIdColumn);
+ mAccountIds[i][1] = newCursor.getLong(activeProviderIdColumn);
+
+ newCursor.moveToNext();
+
+ }
+
+ for (int i = 0; i < mAccountIds.length; i++)
+ initConnection(mAccountIds[i][0],mAccountIds[i][1]);
+
+ mLastAccountId = mAccountIds[0][0];
+ mLastProviderId = mAccountIds[0][1];
+
+ newCursor.moveToFirst();
+
+ initChats();
+
+
+ }
+ else
+ {
+ //no configured accounts, prompt to setup
+ Intent intent = new Intent(NewChatActivity.this, AccountWizardActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ mAccountIds = null;
+ }
+ });
+
+ }
+
+
+ public void unregisterSubListeners ()
+ {
+ if (mAccountIds != null)
+ for (int i = 0; i < mAccountIds.length; i++)
+ {
+ IImConnection conn = initConnection(mAccountIds[i][0],mAccountIds[i][1]);
+ if (conn != null)
+ {
+ try {
+ conn.getContactListManager().unregisterSubscriptionListener(mSubscriptionListener);
+ } catch (RemoteException e1) {
+ Log.e(ImApp.LOG_TAG,"error registering listener",e1);
+
}
-
- posIdx++;
+
}
- while (mCursorChats.moveToNext());
-
-
- return position;
-
- }
- else if (object instanceof ContactListFragment)
- {
- return 0;
-
}
-
- return POSITION_NONE;
-
-
- }
+ }
+ public IImConnection initConnection (long accountId, long providerId)
+ {
- @Override
- public CharSequence getPageTitle(int position) {
-
- if (position == 0)
- {
- return getString(R.string.app_name);
+ IImConnection conn = ((ImApp)getApplication()).getConnection(providerId);
+
+ if (conn == null)
+ {
+ try {
+ conn = ((ImApp)getApplication()).createConnection(providerId, accountId);
+ } catch (RemoteException e) {
+ Log.e(ImApp.LOG_TAG,"error creating connection",e);
}
- else
- {
- int positionMod = position - 1;
- try
- {
- mCursorChats.moveToPosition(positionMod);
- return mCursorChats.getString(ChatView.NICKNAME_COLUMN);
- }
- catch (Exception e)
- {
- mChatPagerAdapter.notifyDataSetChanged();
-
- if (mCursorChats == null)
- {
- Log.e(ImApp.LOG_TAG,"error getting chat",e);
- return "";
- }
- else
- {
- mCursorChats.moveToPosition(positionMod);
- return mCursorChats.getString(ChatView.NICKNAME_COLUMN);
- }
+
+ if (conn != null)
+ {
+
+ try {
+ conn.getContactListManager().registerSubscriptionListener(mSubscriptionListener);
+ // conn.getContactListManager().registerContactListListener(mContactListListener);
+ } catch (RemoteException e1) {
+ Log.e(ImApp.LOG_TAG,"error registering listener",e1);
+
}
}
+
+
}
-
+ return conn;
+
}
-
-
- public static class ContactListFragment extends Fragment implements ContactListListener, ProviderListItem.SignInManager
+
+ public void updateChatList ()
{
+
+ if (mContactList != null && mContactList.mFilterView != null)
+ {
+ mLastPagePosition = -1;
+ Uri baseUri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
+ Uri.Builder builder = baseUri.buildUpon();
+
+ mContactList.mFilterView.doFilter(builder.build(), null);
+ }
+ mChatPager.invalidate();
+ }
+
+
+
+ public static class ContactListFragment extends Fragment implements ContactListListener
+ {
+
private static final String[] PROVIDER_PROJECTION = {
Imps.Provider._ID,
@@ -887,8 +1825,8 @@ public static class ContactListFragment extends Fragment implements ContactListL
Imps.Provider.ACCOUNT_PRESENCE_STATUS,
Imps.Provider.ACCOUNT_CONNECTION_STATUS, };
-
-
+
+
static final int PROVIDER_ID_COLUMN = 0;
static final int PROVIDER_NAME_COLUMN = 1;
static final int PROVIDER_FULLNAME_COLUMN = 2;
@@ -900,433 +1838,243 @@ public static class ContactListFragment extends Fragment implements ContactListL
static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
static final int ACCOUNT_PRESENCE_STATUS = 9;
static final int ACCOUNT_CONNECTION_STATUS = 10;
-
- long[] mAccountIds = null;
+
ContactListFilterView mFilterView = null;
- UserPresenceView mPresenceView = null;
-
- Cursor mProviderCursor = null;
- SignInHelper mSignInHelper = null;
- Spinner mSpinnerAccounts;
+
+ ImApp mApp = null;
private Handler mPresenceHandler = new Handler()
{
-
+
@Override
public void handleMessage(Message msg) {
-
-
- mPresenceView.refreshLogginInStatus();
+
+
+ // mPresenceView.refreshLogginInStatus();
super.handleMessage(msg);
- }
+ }
};
-
- /**
- * When creating, retrieve this instance's number from its arguments.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mSignInHelper = new SignInHelper(getActivity());
-
- }
- /**
+
+ /**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
-
mFilterView = (ContactListFilterView) inflater.inflate(
R.layout.contact_list_filter_view, null);
-
- mPresenceView = (UserPresenceView) mFilterView.findViewById(R.id.userPresence);
+
mFilterView.setListener(this);
-
+ mFilterView.setLoaderManager(getLoaderManager(), CONTACT_LIST_LOADER_ID);
+
TextView txtEmpty = (TextView)mFilterView.findViewById(R.id.empty);
-
+
txtEmpty.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
-
- ((NewChatActivity)getActivity()).showInviteContactDialog();
+
+ ((NewChatActivity)getActivity()).startContactPicker();
}
-
+
});
-
- ((ListView)mFilterView.findViewById(R.id.filteredList)).setEmptyView(txtEmpty);
-
-
- ((ImApp)getActivity().getApplication()).registerForConnEvents(mPresenceHandler);
-
-
- // QueryMap mGlobalSettingMap = new Imps.ProviderSettings.QueryMap(getContext().getContentResolver(), true, mHandler);
-
- // Uri uri = mGlobalSettingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
- // : Imps.Contacts.CONTENT_URI_CONTACTS_BY;
- // uri = ContentUris.withAppendedId(uri, providerId);
- // uri = ContentUris.withAppendedId(uri, accountId);
- // mFilterView.doFilter( Imps.Contacts.CONTENT_URI_CONTACTS_BY, null);
-
- setupSpinners(mFilterView);
-
- setSpinnerState(getActivity());
-
+
+ ((AbsListView)mFilterView.findViewById(R.id.filteredList)).setEmptyView(txtEmpty);
+
+ Uri baseUri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
+ Uri.Builder builder = baseUri.buildUpon();
+ mFilterView.doFilter(builder.build(), null);
+
return mFilterView;
-
+
}
-
-
@Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mProviderCursor != null && (!mProviderCursor.isClosed()))
- mProviderCursor.close();
- }
-
- private void setupSpinners (ContactListFilterView filterView)
- {
-
- mProviderCursor = getActivity().getContentResolver().query(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, PROVIDER_PROJECTION,
- Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL",
-
- new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
- Imps.Provider.DEFAULT_SORT_ORDER);
-
- if (mProviderCursor == null)
- {
- getActivity().finish();
- return;
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
- }
-
- // + " AND " + Imps.Provider.ACCOUNT_CONNECTION_STATUS + " != 0"
-
- /* selection */
- mAccountIds = new long[mProviderCursor.getCount()];
-
-
- mProviderCursor.moveToFirst();
-
- ProviderAdapter pAdapter = new ProviderAdapter(getActivity(), mProviderCursor);
-
- mSpinnerAccounts = (Spinner)filterView.findViewById(R.id.spinnerAccounts);
-
- mSpinnerAccounts.setAdapter(pAdapter);
- mSpinnerAccounts.setOnItemSelectedListener(new OnItemSelectedListener ()
- {
+ mApp = ((ImApp)activity.getApplication());
+ mApp.registerForConnEvents(mPresenceHandler);
- @Override
- public void onItemSelected(AdapterView> parent, View view, int itemPosition, long id) {
-
- // mAccountId = mAccountIds[itemPosition];
- //update account list
- initAccount(getActivity(),mAccountIds[itemPosition]);
-
-
- }
- @Override
- public void onNothingSelected(AdapterView> arg0) {
- // TODO Auto-generated method stub
-
- }
-
- });
-
- mProviderCursor.moveToFirst();
- int activeAccountIdColumn = mProviderCursor.getColumnIndexOrThrow(Imps.Provider.ACTIVE_ACCOUNT_ID);
+ }
- for (int i = 0; i < mAccountIds.length; i++)
- {
- mAccountIds[i] = mProviderCursor.getLong(activeAccountIdColumn);
- mProviderCursor.moveToNext();
-
- }
- }
-
- public void setSpinnerState (Activity activity)
- {
+ @Override
+ public void onDetach() {
+ super.onDetach();
- if (mAccountIds.length == 1) //only one account, hide the spinner
- {
- mSpinnerAccounts.setVisibility(View.GONE);
- initAccount(activity,mAccountIds[0]);
- }
- else if (mAccountId != -1) //multiple accounts, so select a spinner based on user input
- {
+ mApp.unregisterForConnEvents(mPresenceHandler);
+ mApp = null;
- mSpinnerAccounts.setVisibility(View.VISIBLE);
-
- int selIdx = 0;
-
- for (long accountId : mAccountIds)
- {
- if (accountId == mAccountId)
- {
- mSpinnerAccounts.setSelection(selIdx);
- break;
- }
-
- selIdx++;
- }
-
- }
- else if (getActivity() != null) //nothing from the user, show show an active account
- {
- List listConns = ((ImApp)getActivity().getApplication()).getActiveConnections();
-
- for (IImConnection conn : listConns)
- {
- try
- {
- long activeAccountId = conn.getAccountId();
- int spinnerIdx = -1;
- for (long accountId : mAccountIds )
- {
- spinnerIdx++;
-
- if (accountId == activeAccountId)
- {
- mSpinnerAccounts.setSelection(spinnerIdx);
- break;
- }
- }
-
- }
- catch (Exception e){}
- }
- }
-
-
- }
-
- public void initAccount (Activity activity, long accountId)
- {
-
- if (accountId == -1)
- return;
-
- ContentResolver cr = activity.getContentResolver();
- Cursor c = cr.query(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId), null,
- null, null, null);
-
- if (c == null) {
- // finish();
- return;
- }
- if (!c.moveToFirst()) {
- c.close();
- // finish();
- return;
- }
-
- mLastProviderId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
-
- initConnection (activity, accountId, mLastProviderId);
-
- c.close();
- }
-
- private void initConnection (Activity activity, long accountId, long providerId)
- {
- IImConnection conn = ((ImApp)activity.getApplication()).getConnection(providerId);
-
- if (conn == null)
- {
- try {
- conn = ((ImApp)getActivity().getApplication()).createConnection(providerId, accountId);
- } catch (RemoteException e) {
- Log.e(ImApp.LOG_TAG,"error creating connection",e);
- }
- }
-
- if (conn != null)
- {
- mPresenceView.setConnection(conn);
+ }
- try {
- mPresenceView.loggingIn(conn.getState() == ImConnection.LOGGING_IN);
- } catch (RemoteException e) {
- mPresenceView.loggingIn(false);
- // mHandler.showServiceErrorAlert();
- }
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ }
- //mGlobalSettingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
- // : Imps.Contacts.CONTENT_URI_CONTACTS_BY;
- Uri.Builder builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
- ContentUris.appendId(builder, providerId);
- ContentUris.appendId(builder, accountId);
- mFilterView.doFilter(builder.build(), null);
- mChatPagerAdapter.notifyDataSetChanged();
-
- }
-
- }
@Override
- public void startChat(Cursor c) {
-
+ public void openChat(Cursor c) {
+
NewChatActivity activity = (NewChatActivity)getActivity();
-
- if (c != null && activity != null) {
- long chatContactId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
- String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
-
- long providerId = mLastProviderId;//c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
- IImConnection conn = ((ImApp)activity.getApplication()).getConnection(providerId);
-
- if (conn != null)
- {
- try {
- IChatSessionManager manager = conn.getChatSessionManager();
- IChatSession session = manager.getChatSession(username);
- if (session == null) {
- manager.createChatSession(username);
- }
-
- activity.refreshChatViews();
-
- activity.showChat(chatContactId);
-
-
-
- } catch (RemoteException e) {
- // mHandler.showServiceErrorAlert(e.getMessage());
- LogCleaner.debug(ImApp.LOG_TAG, "remote exception starting chat");
+ activity.openExistingChat(c);
- }
-
- }
- else
- {
- LogCleaner.debug(ImApp.LOG_TAG, "could not start chat as connection was null");
- }
- }
-
}
-
+
+ @Override
public void showProfile (Cursor c)
{
if (c != null) {
long chatContactId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
-
+
long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
+ long accountId = c.getLong(c.getColumnIndex(Imps.Contacts.ACCOUNT));
+
Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, chatContactId);
Intent intent = new Intent(Intent.ACTION_VIEW, data);
intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId);
- intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
+ intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, accountId);
startActivity(intent);
-
+
}
}
-
- public class ProviderAdapter extends CursorAdapter {
- private LayoutInflater mInflater;
- @SuppressWarnings("deprecation")
- public ProviderAdapter(Context context, Cursor c) {
- super(context, c);
- mInflater = LayoutInflater.from(context).cloneInContext(context);
- mInflater.setFactory(new ProviderListItemFactory());
- }
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- // create a custom view, so we can manage it ourselves. Mainly, we want to
- // initialize the widget views (by calling getViewById()) in newView() instead of in
- // bindView(), which can be called more often.
- ProviderListItem view = (ProviderListItem) mInflater.inflate(R.layout.account_view_small,
- parent, false);
- view.init(cursor, true);
- return view;
- }
-
-
+ }
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- ((ProviderListItem) view).bindView(cursor);
- }
-
-
-
- }
-
- public class ProviderListItemFactory implements LayoutInflater.Factory {
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- if (name != null && name.equals(ProviderListItem.class.getName())) {
- return new ProviderListItem(context, getActivity(), ContactListFragment.this);
- }
- return null;
- }
-
-
- }
- @Override
- public void signIn(long accountId) {
-
-
- long providerId = mProviderCursor.getLong(PROVIDER_ID_COLUMN);
- String password = mProviderCursor.getString(ACTIVE_ACCOUNT_PW_COLUMN);
-
- boolean isActive = false; // TODO(miron)
- mSignInHelper.signIn(password, mLastProviderId, accountId, isActive);
-
-
+
+
+ private void openExistingChat(Cursor c) {
+
+ if (c != null && (! c.isAfterLast())) {
+ int type = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.TYPE));
+ String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
+ long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
+
+ startChat(providerId,username,type, false, null);
}
+ else
+ updateChatList();
+ }
- @Override
- public void signOut(long accountId) {
-
- IImConnection conn = ((ImApp)getActivity().getApplication()).getConnection(mLastProviderId);
+ private void startChat (long providerId, String address,int userType, boolean isNewChat, String message)
+ {
+ IImConnection conn = mApp.getConnection(providerId);
+
+ if (conn != null)
+ {
try {
- conn.logout();
+ IChatSessionManager manager = conn.getChatSessionManager();
+ IChatSession session = manager.getChatSession(address);
+
+ if (session == null && manager != null) {
+
+ // Create session. Stash requested contact ID for when we get called back.
+ if (userType == Imps.ContactsColumns.TYPE_GROUP)
+ session = manager.createMultiUserChatSession(address, null, isNewChat);
+ else
+ session = manager.createChatSession(address, isNewChat);
+
+ if (session != null)
+ {
+ mRequestedChatId = session.getId();
+
+ if (!showChat(session.getId())) {
+ // We have a session, but it's not in the cursor yet
+ mRequestedChatId = session.getId();
+ session.reInit();
+ }
+ else
+ {
+ mRequestedChatId = -1;//we showed the chat, so set this to -1;
+ }
+
+ if (message != null)
+ session.sendMessage(message);
+ }
+
+ } else {
+ // Already have session
+ if (!showChat(session.getId())) {
+ // We have a session, but it's not in the cursor yet
+ mRequestedChatId = session.getId();
+ session.reInit();
+ }
+
+ }
+
+ updateChatList();
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ // mHandler.showServiceErrorAlert(e.getMessage());
+ LogCleaner.debug(ImApp.LOG_TAG, "remote exception starting chat");
+
}
+
+ }
+ else
+ {
+ LogCleaner.debug(ImApp.LOG_TAG, "could not start chat as connection was null");
}
-
-
-
}
-
-
-
-
+
public static class ChatViewFragment extends Fragment {
-
+
ChatView mChatView;
-
+
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
+ * @param providerId
+ * @param contactName
*/
- static ChatViewFragment newInstance(long chatContactId) {
-
+ static ChatViewFragment newInstance(long chatContactId, String contactName, long providerId) {
+
ChatViewFragment f = new ChatViewFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putLong("contactChatId", chatContactId);
+ args.putString("contactName", contactName);
+ args.putLong("providerId", providerId);
f.setArguments(args);
- return f;
+// Log.d(TAG, "CVF new " + contactName);
+ return f;
+ }
+
+ public ChatViewFragment() {
+// Log.d(TAG, "CVF construct " + super.toString());
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " -> " + getArguments().getString("contactName");
+ }
+
+ public void onSelected(ImApp app) {
+
+ //app.dismissChatNotification(getArguments().getLong("providerId"), getArguments().getString("contactName"));
+
+ if (mChatView != null)
+ mChatView.setSelected(true);
+
+
+ }
+
+ public void onDeselected(ImApp app) {
+ if (mChatView != null)
+ mChatView.setSelected(false);
}
/**
@@ -1335,8 +2083,8 @@ static ChatViewFragment newInstance(long chatContactId) {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
-
+
+// Log.d(TAG, "CVF create " + getArguments().getString("contactName"));
}
/**
@@ -1346,140 +2094,279 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
-
+
long chatContactId = getArguments().getLong("contactChatId");
mChatView = (ChatView)inflater.inflate(R.layout.chat_view, container, false);
- mChatView.bindChat(chatContactId);
-
+ mChatView.bindChat(chatContactId);
+
return mChatView;
}
+ public void onServiceConnected() {
+ if (isResumed()) {
+ mChatView.onServiceConnected();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mChatView.startListening();
+ }
+
@Override
public void onPause() {
super.onPause();
-
- if (mChatView != null)
- mChatView.stopListening();
-
+
+ mChatView.stopListening();
}
@Override
- public void onResume() {
- super.onResume();
-
- if (mChatView != null)
- mChatView.startListening();
+ public void onDestroy() {
+ mChatView.unbind();
+ super.onDestroy();
}
-
+ public ChatView getChatView() {
+ return mChatView;
+ }
}
-
+
public ChatView getCurrentChatView ()
{
int cItemIdx;
-
- if ((cItemIdx = mChatPager.getCurrentItem()) > 0)
+
+ // FIXME why is mChatPagerAdapter null here? Is this called after onDestroy?
+ if (mChatPagerAdapter != null && (cItemIdx = mChatPager.getCurrentItem()) > 0)
{
- return (ChatView)((ChatViewFragment)mChatPagerAdapter.getItem(cItemIdx)).getView();
+ return mChatPagerAdapter.getChatViewAt(cItemIdx);
}
else
return null;
}
-
-
+
+
+ View mDialogGroup = null;
private void showGroupChatDialog ()
{
- ContentResolver cr = getContentResolver();
-
- Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
- cr, mLastProviderId, false /* don't keep updated */, null /* no handler */);
- String chatDomain = "conference." + settings.getDomain();
-
- settings.close();
-
-
-
// This example shows how to add a custom layout to an AlertDialog
LayoutInflater factory = LayoutInflater.from(this);
- final View textEntryView = factory.inflate(R.layout.alert_dialog_group_chat, null);
- final TextView tvServer = (TextView) textEntryView.findViewById(R.id.chat_server);
+
+ mDialogGroup = factory.inflate(R.layout.alert_dialog_group_chat, null);
- tvServer.setText(chatDomain);
+ final Spinner listAccounts = (Spinner) mDialogGroup.findViewById(R.id.choose_list);
+ setupAccountSpinner(listAccounts);
- new AlertDialog.Builder(this)
+ new AlertDialog.Builder(this)
.setTitle(R.string.create_or_join_group_chat)
- .setView(textEntryView)
+ .setView(mDialogGroup)
.setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked OK so do some stuff */
-
+
String chatRoom = null;
String chatServer = null;
-
- TextView tv = (TextView)textEntryView.findViewById(R.id.chat_room);
-
+ String nickname = null;
+
+ TextView tv = (TextView)mDialogGroup.findViewById(R.id.chat_room);
chatRoom = tv.getText().toString();
-
- tv = (TextView) textEntryView.findViewById(R.id.chat_server);
-
+
+ tv = (TextView) mDialogGroup.findViewById(R.id.chat_server);
chatServer = tv.getText().toString();
-
- startGroupChat (chatRoom, chatServer, ((ImApp)getApplication()).getConnection(mLastProviderId));
-
+
+ tv = (TextView) mDialogGroup.findViewById(R.id.nickname);
+ nickname = tv.getText().toString();
+
+ try
+ {
+ IImConnection conn = mApp.getConnection(mLastProviderId);
+ if (conn.getState() == ImConnection.LOGGED_IN)
+ startGroupChat (chatRoom, chatServer, nickname, conn);
+ else
+ {
+ //can't start group chat
+ mHandler.showAlert("Group Chat","Please enable your account to join a group chat");
+ }
+ } catch (RemoteException re) {
+
+ }
+
+ dialog.dismiss();
+
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
+ dialog.dismiss();
}
})
.create().show();
-
-
-
+
+
+
}
-
- public void startGroupChat (String room, String server, IImConnection conn)
+
+ private void setupAccountSpinner (Spinner spinner)
{
- String roomAddress = room + '@' + server;
-
- try {
- IChatSessionManager manager = conn.getChatSessionManager();
- IChatSession session = manager.getChatSession(roomAddress);
- if (session == null) {
- session = manager.createMultiUserChatSession(roomAddress);
+ final Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT;
+
+ final Cursor cursorProviders = managedQuery(uri, PROVIDER_PROJECTION,
+ Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL" /* selection */,
+ new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
+ Imps.Provider.DEFAULT_SORT_ORDER);
+
+ SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
+ android.R.layout.simple_spinner_dropdown_item, cursorProviders, new String[] { Imps.Provider.ACTIVE_ACCOUNT_USERNAME},
+ new int[] { android.R.id.text1 });
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ if (cursorProviders.getCount() > 0)
+ {
+ cursorProviders.moveToFirst();
+ mLastProviderId = cursorProviders.getLong(PROVIDER_ID_COLUMN);
+ mLastAccountId = cursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+
+ spinner.setAdapter(adapter);
+ spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView> arg0, View arg1,
+ int arg2, long arg3) {
+ cursorProviders.moveToPosition(arg2);
+
+ mLastProviderId = cursorProviders.getLong(PROVIDER_ID_COLUMN);
+ mLastAccountId = cursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+
+ mHandler.post(new Runnable()
+ {
+
+ public void run ()
+ {
+ TextView tvServer = (TextView) mDialogGroup.findViewById(R.id.chat_server);
+
+ IChatSessionManager manager;
+ try {
+ IImConnection conn = mApp.getConnection(mLastProviderId);
+ manager = conn.getChatSessionManager();
+ String server = manager.getDefaultMultiUserChatServer();
+ if (server != null)
+ tvServer.setText(server);
+ } catch (RemoteException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ });
+
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+ else
+ {
+ spinner.setVisibility(View.GONE);
+ }
+
+ }
+
+
+
+ private IImConnection mLastConnGroup = null;
+
+ public void startGroupChat (String room, String server, String nickname, IImConnection conn)
+ {
+ mLastConnGroup = conn;
+
+ new AsyncTask() {
+
+ private ProgressDialog dialog;
+
+
+ @Override
+ protected void onPreExecute() {
+ dialog = new ProgressDialog(NewChatActivity.this);
+
+ dialog.setMessage(getString(R.string.connecting_to_group_chat_));
+ dialog.setCancelable(true);
+ dialog.show();
}
- if (session != null)
- {
- long id = session.getId();
-
- Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, id);
- Intent i = new Intent(Intent.ACTION_VIEW, data);
- i.addCategory(ImApp.IMPS_CATEGORY);
-
- if (menu.isShown())
- menu.toggle();
-
- startActivity(i);
+ @Override
+ protected String doInBackground(String... params) {
+
+ String roomAddress = (params[0] + '@' + params[1]).toLowerCase(Locale.US).replace(' ', '_');
+ String nickname = params[2];
+
+ try {
+ IChatSessionManager manager = mLastConnGroup.getChatSessionManager();
+ IChatSession session = manager.getChatSession(roomAddress);
+ if (session == null) {
+ session = manager.createMultiUserChatSession(roomAddress, nickname, true);
+
+ if (session != null)
+ {
+ mRequestedChatId = session.getId();
+ publishProgress(mRequestedChatId);
+
+ } else {
+ return getString(R.string.unable_to_create_or_join_group_chat);
+
+ }
+ } else {
+ mRequestedChatId = session.getId();
+ publishProgress(mRequestedChatId);
+ }
+
+ return null;
+
+ } catch (RemoteException e) {
+ return e.toString();
+ }
+
+ }
+
+ @Override
+ protected void onProgressUpdate(Long... showChatId) {
+ showChat(showChatId[0]);
}
- else
- {
- mHandler.showServiceErrorAlert(getString(R.string.unable_to_create_or_join_group_chat));
-
+
+ @Override
+ protected void onPostExecute(String result) {
+ super.onPostExecute(result);
+
+ if (dialog.isShowing()) {
+ dialog.dismiss();
+ }
+
+ if (result != null)
+ {
+ mHandler.showServiceErrorAlert(result);
+
+ }
+
+
}
-
- } catch (RemoteException e) {
- mHandler.showServiceErrorAlert(getString(R.string.unable_to_create_or_join_group_chat));
- }
-
+ }.execute(room, server, nickname);
+
+
+
}
-
+
void acceptInvitation(long providerId, long invitationId) {
try {
@@ -1503,40 +2390,59 @@ void declineInvitation(long providerId, long invitationId) {
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
- LogCleaner.error(ImApp.LOG_TAG, "decline invite error",e);
+ LogCleaner.error(ImApp.LOG_TAG, "decline invite error",e);
}
}
-
+
void showSubscriptionDialog (final long subProviderId, final String subFrom)
{
- new AlertDialog.Builder(this)
- .setTitle(getString(R.string.subscriptions))
- .setMessage(getString(R.string.subscription_prompt,subFrom))
- .setPositiveButton(R.string.approve_subscription, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
+ if (! ((Activity) this).isFinishing()) {
- approveSubscription(subProviderId, subFrom);
- }
- })
- .setNegativeButton(R.string.decline_subscription, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
+ mHandler.postDelayed(new Runnable()
+ {
- declineSubscription(subProviderId, subFrom);
- }
- })
- .create().show();
+ @Override
+ public void run ()
+ {
+ new AlertDialog.Builder(NewChatActivity.this)
+ .setTitle(getString(R.string.subscriptions))
+ .setMessage(getString(R.string.subscription_prompt,subFrom))
+ .setPositiveButton(R.string.approve_subscription, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+
+ approveSubscription(subProviderId, subFrom);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.decline_subscription, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+
+ declineSubscription(subProviderId, subFrom);
+ dialog.dismiss();
+ }
+ })
+ .create().show();
+ }
+ },500);
+ }
}
void approveSubscription(long providerId, String userName) {
IImConnection conn = mApp.getConnection(providerId);
- try {
- IContactListManager manager = conn.getContactListManager();
- manager.approveSubscription(userName);
- } catch (RemoteException e) {
+ if (conn != null)
+ {
+ try {
+ IContactListManager manager = conn.getContactListManager();
- mHandler.showServiceErrorAlert(e.getLocalizedMessage());
- LogCleaner.error(ImApp.LOG_TAG, "approve sub error",e);
+ manager.approveSubscription(new Contact(new XmppAddress(userName),userName));
+ } catch (RemoteException e) {
+
+ mHandler.showServiceErrorAlert(e.getLocalizedMessage());
+ LogCleaner.error(ImApp.LOG_TAG, "approve sub error",e);
+ }
}
}
@@ -1547,13 +2453,155 @@ void declineSubscription(long providerId, String userName) {
{
try {
IContactListManager manager = conn.getContactListManager();
- manager.declineSubscription(userName);
+ manager.declineSubscription(new Contact(new XmppAddress(userName),userName));
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "decline sub error",e);
}
}
}
+
+
+ long getLastAccountId() {
+ return mLastAccountId;
+ }
+
+ long getLastProviderId() {
+ return mLastProviderId;
+ }
+
+ void setLastProviderId(long mLastProviderId) {
+ this.mLastProviderId = mLastProviderId;
+ }
+
+ public class ProviderListItemFactory implements LayoutInflater.Factory {
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ if (name != null && name.equals(ProviderListItem.class.getName())) {
+ return new ProviderListItem(context, NewChatActivity.this, null);
+ }
+ return null;
+ }
+
+ }
+
+ private final ISubscriptionListener.Stub mSubscriptionListener = new ISubscriptionListener.Stub() {
+
+ @Override
+ public void onSubScriptionRequest(Contact from, long providerId, long accountId) {
+
+ showSubscriptionDialog (providerId, from.getAddress().getAddress());
+
+ }
+
+ @Override
+ public void onSubscriptionApproved(Contact contact, long providerId, long accountId) {
+
+ }
+
+ @Override
+ public void onSubscriptionDeclined(Contact contact, long providerId, long accountId) {
+
+ }
+
+ };
+ private final IContactListListener.Stub mContactListListener = new IContactListListener.Stub ()
+ {
+
+ @Override
+ public IBinder asBinder() {
+
+ return null;
+ }
+
+ @Override
+ public void onContactChange(int type, IContactList list, Contact contact)
+ throws RemoteException {
+
+
+ }
+
+ @Override
+ public void onAllContactListsLoaded() throws RemoteException {
+
+ Log.d(ImApp.LOG_TAG, "onAllContactListsLoaded");
+ }
+
+ @Override
+ public void onContactsPresenceUpdate(Contact[] contacts) throws RemoteException {
+
+
+ }
+
+ @Override
+ public void onContactError(int errorType, ImErrorInfo error, String listName,
+ Contact contact) throws RemoteException {
+
+
+ }
+
+ };
+
+ private synchronized Imps.ProviderSettings.QueryMap getGlobalSettings() {
+ if (mGlobalSettings == null) {
+
+ ContentResolver contentResolver = getContentResolver();
+
+ Cursor cursor = contentResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
+ if (cursor == null)
+ return null;
+
+ mGlobalSettings = new Imps.ProviderSettings.QueryMap(cursor, contentResolver, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, true, mHandler);
+ }
+
+ return mGlobalSettings;
+ }
+
+ public int getOtrPolicy() {
+ int otrPolicy = OtrPolicy.OPPORTUNISTIC;
+
+ String otrModeSelect = getGlobalSettings().getOtrMode();
+
+ if (otrModeSelect.equals("auto")) {
+ otrPolicy = OtrPolicy.OPPORTUNISTIC;
+ } else if (otrModeSelect.equals("disabled")) {
+ otrPolicy = OtrPolicy.NEVER;
+
+ } else if (otrModeSelect.equals("force")) {
+ otrPolicy = OtrPolicy.OTRL_POLICY_ALWAYS;
+
+ } else if (otrModeSelect.equals("requested")) {
+ otrPolicy = OtrPolicy.OTRL_POLICY_MANUAL;
+ }
+ return otrPolicy;
+ }
+
+ private static final String[] PROVIDER_PROJECTION = {
+ Imps.Provider._ID,
+ Imps.Provider.NAME,
+ Imps.Provider.FULLNAME,
+ Imps.Provider.CATEGORY,
+ Imps.Provider.ACTIVE_ACCOUNT_ID,
+ Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
+ Imps.Provider.ACTIVE_ACCOUNT_PW,
+ Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
+ Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
+ Imps.Provider.ACCOUNT_PRESENCE_STATUS,
+ Imps.Provider.ACCOUNT_CONNECTION_STATUS
+ };
+
+ static final int PROVIDER_ID_COLUMN = 0;
+ static final int PROVIDER_NAME_COLUMN = 1;
+ static final int PROVIDER_FULLNAME_COLUMN = 2;
+ static final int PROVIDER_CATEGORY_COLUMN = 3;
+ static final int ACTIVE_ACCOUNT_ID_COLUMN = 4;
+ static final int ACTIVE_ACCOUNT_USERNAME_COLUMN = 5;
+ static final int ACTIVE_ACCOUNT_PW_COLUMN = 6;
+ static final int ACTIVE_ACCOUNT_LOCKED = 7;
+ static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
+ static final int ACCOUNT_PRESENCE_STATUS = 9;
+ static final int ACCOUNT_CONNECTION_STATUS = 10;
}
diff --git a/src/info/guardianproject/otr/app/im/app/PanicResponderActivity.java b/src/info/guardianproject/otr/app/im/app/PanicResponderActivity.java
new file mode 100644
index 000000000..c4b85394d
--- /dev/null
+++ b/src/info/guardianproject/otr/app/im/app/PanicResponderActivity.java
@@ -0,0 +1,31 @@
+
+package info.guardianproject.otr.app.im.app;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+
+public class PanicResponderActivity extends Activity {
+
+ public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
+
+ @SuppressLint("NewApi")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
+ WelcomeActivity.shutdownAndLock(this);
+ ExitActivity.exitAndRemoveFromRecentApps(this);
+ }
+
+ if (Build.VERSION.SDK_INT >= 21) {
+ finishAndRemoveTask();
+ } else {
+ finish();
+ }
+ }
+}
diff --git a/src/info/guardianproject/otr/app/im/app/PresenceUtils.java b/src/info/guardianproject/otr/app/im/app/PresenceUtils.java
index 27645f3d7..e08f4befc 100644
--- a/src/info/guardianproject/otr/app/im/app/PresenceUtils.java
+++ b/src/info/guardianproject/otr/app/im/app/PresenceUtils.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/ProviderDef.java b/src/info/guardianproject/otr/app/im/app/ProviderDef.java
index c92d7820d..ecbd5e215 100644
--- a/src/info/guardianproject/otr/app/im/app/ProviderDef.java
+++ b/src/info/guardianproject/otr/app/im/app/ProviderDef.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/ProviderListItem.java b/src/info/guardianproject/otr/app/im/app/ProviderListItem.java
index d37450a6a..708ab4b89 100644
--- a/src/info/guardianproject/otr/app/im/app/ProviderListItem.java
+++ b/src/info/guardianproject/otr/app/im/app/ProviderListItem.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2009 Myriad Group AG Copyright (C) 2009 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,102 +17,83 @@
package info.guardianproject.otr.app.im.app;
+import info.guardianproject.otr.app.im.IImConnection;
import info.guardianproject.otr.app.im.R;
-import info.guardianproject.otr.app.im.plugin.BrandingResourceIDs;
+import info.guardianproject.otr.app.im.engine.ImConnection;
import info.guardianproject.otr.app.im.provider.Imps;
-import info.guardianproject.otr.app.im.service.ImServiceConstants;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.drawable.Drawable;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
import android.util.Log;
-import android.view.MotionEvent;
import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ProviderListItem extends LinearLayout {
- private static final String TAG = "IM";
- private static final boolean LOCAL_DEBUG = false;
-
private Activity mActivity;
- private SignInManager mSignInManager;
-
- private CompoundButton mSignInSwitch;
- private OnCheckedChangeListener mCheckedChangeListner = new OnCheckedChangeListener(){
+ //private SignInManager mSignInManager;
+ private ContentResolver mResolver;
+ // private CompoundButton mSignInSwitch;
+
+ //private boolean mUserChanged = false;
+ private boolean mIsSignedIn;
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-
- if (isChecked)
- mSignInManager.signIn(mAccountId);
- else
- mSignInManager.signOut(mAccountId);
-
- mUserChanged = true;
- }
-
- };
- private boolean mUserChanged = false;
-
private TextView mProviderName;
private TextView mLoginName;
- private TextView mChatView;
- private View mUnderBubble;
- private Drawable mBubbleDrawable;
- private Drawable mDefaultBackground;
- private ImageView mBtnSettings;
-
private int mProviderIdColumn;
- private int mProviderFullnameColumn;
private int mActiveAccountIdColumn;
private int mActiveAccountUserNameColumn;
private int mAccountPresenceStatusColumn;
private int mAccountConnectionStatusColumn;
- private ColorStateList mProviderNameColors;
- private ColorStateList mLoginNameColors;
- private ColorStateList mChatViewColors;
-
private long mAccountId;
private boolean mShowLongName = false;
-
+ private ImApp mApp = null;
+
+ private static Handler mHandler = new Handler()
+ {
+
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+
+ //update notifications from async task
+ }
+
+ };
+
public ProviderListItem(Context context, Activity activity, SignInManager signInManager) {
super(context);
mActivity = activity;
- mSignInManager = signInManager;
+ //mSignInManager = signInManager;
+
+ mApp = (ImApp)activity.getApplication();
+
+ mResolver = mApp.getContentResolver();
+
}
public void init(Cursor c, boolean showLongName) {
-
+
mShowLongName = showLongName;
-
+
mProviderIdColumn = c.getColumnIndexOrThrow(Imps.Provider._ID);
- //mProviderIcon = (ImageView) findViewById(R.id.providerIcon);
- // mStatusIcon = (ImageView) findViewById(R.id.statusIcon);
- mSignInSwitch = (CompoundButton) findViewById(R.id.statusSwitch);
+ //mSignInSwitch = (CompoundButton) findViewById(R.id.statusSwitch);
mProviderName = (TextView) findViewById(R.id.providerName);
mLoginName = (TextView) findViewById(R.id.loginName);
- mChatView = (TextView) findViewById(R.id.conversations);
- mUnderBubble = findViewById(R.id.underBubble);
- mBubbleDrawable = getResources().getDrawable(R.drawable.bubble);
- mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
-
- mBtnSettings = (ImageView)findViewById(R.id.btnSettings);
-
- mProviderFullnameColumn = c.getColumnIndexOrThrow(Imps.Provider.FULLNAME);
+
mActiveAccountIdColumn = c.getColumnIndexOrThrow(Imps.Provider.ACTIVE_ACCOUNT_ID);
mActiveAccountUserNameColumn = c
.getColumnIndexOrThrow(Imps.Provider.ACTIVE_ACCOUNT_USERNAME);
@@ -121,14 +102,25 @@ public void init(Cursor c, boolean showLongName) {
mAccountConnectionStatusColumn = c
.getColumnIndexOrThrow(Imps.Provider.ACCOUNT_CONNECTION_STATUS);
- mProviderNameColors = mProviderName.getTextColors();
-
- if (mLoginName != null)
- mLoginNameColors = mLoginName.getTextColors();
-
- if (mChatView != null)
- mChatViewColors = mChatView.getTextColors();
-
+ setOnClickListener(new OnClickListener ()
+ {
+
+ @Override
+ public void onClick(View v) {
+
+
+ Intent intent = new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(
+ Imps.Account.CONTENT_URI, mAccountId));
+ intent.addCategory(ImApp.IMPS_CATEGORY);
+
+ intent.putExtra("isSignedIn", mIsSignedIn);
+
+ mActivity.startActivity(intent);
+ }
+
+ });
+
+ /*
if (mSignInSwitch != null)
{
mProviderName.setOnClickListener(new OnClickListener ()
@@ -136,212 +128,271 @@ public void init(Cursor c, boolean showLongName) {
@Override
public void onClick(View v) {
-
- Intent intent = new Intent(getContext(), NewChatActivity.class);
- intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
- getContext().startActivity(intent);
+
+ Intent intent = new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(
+ Imps.Account.CONTENT_URI, mAccountId));
+ intent.addCategory(ImApp.IMPS_CATEGORY);
+ mActivity.startActivity(intent);
}
-
+
});
-
+
mLoginName.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
-
- Intent intent = new Intent(getContext(), NewChatActivity.class);
- intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
- getContext().startActivity(intent);
+
+ Intent intent = new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(
+ Imps.Account.CONTENT_URI, mAccountId));
+ intent.addCategory(ImApp.IMPS_CATEGORY);
+ mActivity.startActivity(intent);
}
-
+
});
-
- mSignInSwitch.setOnCheckedChangeListener(mCheckedChangeListner);
-
-
- if (mBtnSettings != null)
- {
- mBtnSettings.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v) {
-
- Intent intent = new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(
- Imps.Account.CONTENT_URI, mAccountId));
- intent.addCategory(ImApp.IMPS_CATEGORY);
- mActivity.startActivity(intent);
- }
-
- });
- }
+ mSignInSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener(){
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked)
+ mSignInManager.signIn(mAccountId);
+ else
+ mSignInManager.signOut(mAccountId);
+
+ mUserChanged = true;
+ }
+
+ });
+
+
}
-
-/*
+ */
+
+/*
mStatusSwitch.setOnClickListener(new OnClickListener (){
@Override
public void onClick(View v) {
-
+
if (mStatusSwitch.isChecked())
mSignInManager.signIn(mAccountId);
else
mSignInManager.signOut(mAccountId);
-
+
}
-
+
});*/
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
public void bindView(Cursor cursor) {
- Resources r = getResources();
- // ImageView providerIcon = mProviderIcon;
-
- int providerId = cursor.getInt(mProviderIdColumn);
- String providerDisplayName = cursor.getString(mProviderFullnameColumn);
-
- final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(getContext().getContentResolver(),
- providerId, false , null);
-
- String userDomain = settings.getDomain();
-
-
+ final Resources r = getResources();
+
+ final int providerId = cursor.getInt(mProviderIdColumn);
+
mAccountId = cursor.getLong(mActiveAccountIdColumn);
setTag(mAccountId);
-
- ImApp app = (ImApp)mActivity.getApplication();
- if (mUnderBubble != null)
- mUnderBubble.setBackgroundDrawable(mDefaultBackground);
+ if (!cursor.isNull(mActiveAccountIdColumn)) {
- mProviderName.setTextColor(mProviderNameColors);
-
- if (mLoginNameColors != null)
- mLoginName.setTextColor(mLoginNameColors);
-
- if (mChatViewColors != null)
- mChatView.setTextColor(mChatViewColors);
+ final String activeUserName = cursor.getString(mActiveAccountUserNameColumn);
- if (!cursor.isNull(mActiveAccountIdColumn)) {
-
- String activeUserName = cursor.getString(mActiveAccountUserNameColumn);
-
- if (mShowLongName)
- mProviderName.setText(activeUserName + '@' + userDomain);
- else
- mProviderName.setText(activeUserName);
+ final int connectionStatus = cursor.getInt(mAccountConnectionStatusColumn);
+ final String presenceString = getPresenceString(cursor, getContext());
-
- int connectionStatus = cursor.getInt(mAccountConnectionStatusColumn);
+ mHandler.postDelayed(new Runnable () {
+ public void run ()
+ {
+ runBindTask(r, providerId, activeUserName, connectionStatus, presenceString);
+ }
+ }
+ , 200l);
- StringBuffer secondRowText = new StringBuffer();
+ }
+ }
- mChatView.setVisibility(View.GONE);
+ @Override
+ protected void onDetachedFromWindow() {
- switch (connectionStatus) {
-
- case Imps.ConnectionStatus.CONNECTING:
- secondRowText.append(r.getString(R.string.signing_in_wait));
+ super.onDetachedFromWindow();
+ }
- if (mSignInSwitch != null && (!mUserChanged))
- {
- mSignInSwitch.setOnCheckedChangeListener(null);
- mSignInSwitch.setChecked(true);
- mSignInSwitch.setOnCheckedChangeListener(mCheckedChangeListner);
- }
-
- break;
+ private void runBindTask(final Resources r, final int providerId, final String activeUserName,
+ final int dbConnectionStatus, final String presenceString) {
- case Imps.ConnectionStatus.ONLINE:
-
- if (mSignInSwitch != null && (!mUserChanged))
- {
- mSignInSwitch.setOnCheckedChangeListener(null);
- mSignInSwitch.setChecked(true);
- mSignInSwitch.setOnCheckedChangeListener(mCheckedChangeListner);
- }
-
-
- secondRowText.append(getPresenceString(cursor, getContext()));
+ String mProviderNameText;
+ String mSecondRowText;
- secondRowText.append(" - ");
-
- if (settings.getServer() != null && settings.getServer().length() > 0)
+ try
+ {
+ Cursor pCursor = mResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( providerId)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, mResolver,
+ providerId, false /* keep updated */, mHandler /* no handler */);
+
+ String userDomain = settings.getDomain();
+ int connectionStatus = dbConnectionStatus;
+
+ IImConnection conn = mApp.getConnection(providerId);
+ if (conn == null)
{
- secondRowText.append(settings.getServer());
-
+ connectionStatus = ImConnection.DISCONNECTED;
}
else
{
- secondRowText.append(settings.getDomain());
+ try {
+ connectionStatus = conn.getState();
+ } catch (RemoteException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
-
-
- if (settings.getPort() != 5222 && settings.getPort() != 0)
- secondRowText.append(':').append(settings.getPort());
-
-
- if (settings.getUseTor())
- {
- secondRowText.append(" - ");
- secondRowText.append(r.getString(R.string._via_orbot));
+
+ if (mShowLongName)
+ mProviderNameText = activeUserName + '@' + userDomain;
+ else
+ mProviderNameText = activeUserName;
+
+ switch (connectionStatus) {
+
+ case ImConnection.LOGGING_IN:
+ mSecondRowText = r.getString(R.string.signing_in_wait);
+ mIsSignedIn = true;
+
+ break;
+
+ case ImConnection.SUSPENDING:
+ case ImConnection.SUSPENDED:
+ mSecondRowText = r.getString(R.string.error_suspended_connection);
+ mIsSignedIn = true;
+
+ break;
+
+
+
+ case ImConnection.LOGGED_IN:
+ mIsSignedIn = true;
+ mSecondRowText = computeSecondRowText(presenceString, r, settings, true);
+
+ break;
+
+ case ImConnection.LOGGING_OUT:
+ mIsSignedIn = false;
+ mSecondRowText = r.getString(R.string.signing_out_wait);
+
+ break;
+
+ default:
+
+ mIsSignedIn = false;
+ mSecondRowText = computeSecondRowText(presenceString, r, settings, true);
+ break;
}
-
-
-
- break;
- default:
-
+ settings.close();
+ pCursor.close();
- if (mSignInSwitch != null && (!mUserChanged))
- {
- mSignInSwitch.setOnCheckedChangeListener(null);
- mSignInSwitch.setChecked(false);
- mSignInSwitch.setOnCheckedChangeListener(mCheckedChangeListner);
+ applyView(mProviderNameText, mIsSignedIn, mSecondRowText);
}
-
- if (settings.getServer() != null && settings.getServer().length() > 0)
+ catch (NullPointerException npe)
{
- secondRowText.append(settings.getServer());
-
+ Log.d(ImApp.LOG_TAG,"null on QueryMap (this shouldn't happen anymore, but just in case)",npe);
}
+
+
+
+
+ }
+
+ private void applyView(String providerNameText, boolean isSignedIn, String secondRowText) {
+
+ if (isSignedIn)
+ {
+ setBackgroundColor(getResources().getColor(R.color.holo_blue_dark));
+ }
+ else
+ {
+ setBackgroundColor(getResources().getColor(android.R.color.transparent));
+ }
+
+ if (mProviderName != null)
+ {
+ mProviderName.setText(providerNameText);
+
+ if (isSignedIn)
+ mProviderName.setTextColor(Color.WHITE);
+ else
+ mProviderName.setTextColor(Color.LTGRAY);
+
+
+ if (mLoginName != null)
+ {
+ mLoginName.setText(secondRowText);
+
+ if (isSignedIn)
+ mLoginName.setTextColor(Color.WHITE);
else
- {
- secondRowText.append(settings.getDomain());
- }
-
-
- if (settings.getPort() != 5222 && settings.getPort() != 0)
- secondRowText.append(':').append(settings.getPort());
-
-
-
- if (settings.getUseTor())
- {
- secondRowText.append(" - ");
- secondRowText.append(r.getString(R.string._via_orbot));
- }
-
- break;
+ mLoginName.setTextColor(Color.LTGRAY);
+
+
}
+ }
+
+ }
+
+ private String computeSecondRowText(String presenceString, Resources r,
+ final Imps.ProviderSettings.QueryMap settings, boolean showPresence) {
+ String secondRowText;
+ StringBuffer secondRowTextBuffer = new StringBuffer();
- mLoginName.setText(secondRowText);
- }
-
- settings.close();
+ if (showPresence && presenceString.length() > 0)
+ {
+ secondRowTextBuffer.append(presenceString);
+ secondRowTextBuffer.append(" - ");
+ }
+
+
+ if (settings.getServer() != null && settings.getServer().length() > 0)
+ {
+
+ secondRowTextBuffer.append(settings.getServer());
+
+ }
+ else if (settings.getDomain() != null & settings.getDomain().length() > 0)
+ {
+ secondRowTextBuffer.append(settings.getDomain());
+ }
+
+
+ if (settings.getPort() != 5222 && settings.getPort() != 0)
+ secondRowTextBuffer.append(':').append(settings.getPort());
+
+
+ if (settings.getUseTor())
+ {
+ secondRowTextBuffer.append(" - ");
+ secondRowTextBuffer.append(r.getString(R.string._via_orbot));
+ }
+
+ secondRowText = secondRowTextBuffer.toString();
+ return secondRowText;
}
-
+
public Long getAccountID ()
{
return mAccountId;
}
-
+
private String getPresenceString(Cursor cursor, Context context) {
int presenceStatus = cursor.getInt(mAccountPresenceStatusColumn);
@@ -353,7 +404,7 @@ private String getPresenceString(Cursor cursor, Context context) {
case Imps.Presence.IDLE:
return context.getString(R.string.presence_idle);
-
+
case Imps.Presence.AWAY:
return context.getString(R.string.presence_away);
@@ -365,44 +416,18 @@ private String getPresenceString(Cursor cursor, Context context) {
return context.getString(R.string.presence_invisible);
default:
- return context.getString(R.string.presence_offline);
+ return "";
}
}
- private int getPresenceIconId(Cursor cursor) {
- int presenceStatus = cursor.getInt(mAccountPresenceStatusColumn);
-
- if (LOCAL_DEBUG)
- log("getPresenceIconId: presenceStatus=" + presenceStatus);
-
- switch (presenceStatus) {
- case Imps.Presence.AVAILABLE:
- return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
-
- case Imps.Presence.IDLE:
- case Imps.Presence.AWAY:
- return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
-
- case Imps.Presence.DO_NOT_DISTURB:
- return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
-
- case Imps.Presence.INVISIBLE:
- return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
-
- default:
- return BrandingResourceIDs.DRAWABLE_PRESENCE_OFFLINE;
- }
- }
-
- private void log(String msg) {
- Log.d(TAG, msg);
- }
-
public interface SignInManager
{
public void signIn (long accountId);
public void signOut (long accountId);
- };
+ }
+
+
+
}
diff --git a/src/info/guardianproject/otr/app/im/app/SettingActivity.java b/src/info/guardianproject/otr/app/im/app/SettingActivity.java
index dba0d27a4..783a6f99f 100644
--- a/src/info/guardianproject/otr/app/im/app/SettingActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/SettingActivity.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,16 +17,17 @@
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.otr.app.im.R;
-import info.guardianproject.otr.app.im.provider.Imps;
-import info.guardianproject.otr.app.im.provider.Imps.ProviderSettings;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
@@ -34,64 +35,138 @@
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.text.TextUtils;
-import com.actionbarsherlock.app.SherlockPreferenceActivity;
+import info.guardianproject.otr.app.im.R;
+import info.guardianproject.otr.app.im.provider.Imps;
+import info.guardianproject.util.Languages;
-public class SettingActivity extends SherlockPreferenceActivity implements
+public class SettingActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
+ private static final String TAG = "SettingActivity";
private static final int DEFAULT_HEARTBEAT_INTERVAL = 1;
+ private String currentLanguage;
ListPreference mOtrMode;
+ ListPreference mLanguage;
+ CheckBoxPreference mLinkifyOnTor;
CheckBoxPreference mHideOfflineContacts;
+ CheckBoxPreference mDeleteUnsecuredMedia;
+ CheckBoxPreference mStoreMediaOnExternalStorage;
CheckBoxPreference mEnableNotification;
CheckBoxPreference mNotificationVibrate;
CheckBoxPreference mNotificationSound;
- // CheckBoxPreference mForegroundService;
+ CheckBoxPreference mForegroundService;
EditTextPreference mHeartbeatInterval;
-
+
EditTextPreference mThemeBackground;
+ Preference mNotificationRingtone;
private void setInitialValues() {
ContentResolver cr = getContentResolver();
- Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(cr,
- false /* keep updated */, null /* no handler */);
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, cr,
+ Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, false /* keep updated */, null /* no handler */);
mOtrMode.setValue(settings.getOtrMode());
+ mLinkifyOnTor.setChecked(settings.getLinkifyOnTor());
mHideOfflineContacts.setChecked(settings.getHideOfflineContacts());
+ mDeleteUnsecuredMedia.setChecked(settings.getDeleteUnsecuredMedia());
mEnableNotification.setChecked(settings.getEnableNotification());
mNotificationVibrate.setChecked(settings.getVibrate());
mNotificationSound.setChecked(settings.getRingtoneURI() != null);
-
- //mForegroundService.setChecked(settings.getUseForegroundPriority());
-
+
+ mForegroundService.setChecked(settings.getUseForegroundPriority());
+
long heartbeatInterval = settings.getHeartbeatInterval();
if (heartbeatInterval == 0) heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL;
mHeartbeatInterval.setText(String.valueOf(heartbeatInterval));
settings.close();
+
+ /* This uses SharedPreferences since it is used before Imps is setup */
+ SharedPreferences sharedPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+ mStoreMediaOnExternalStorage.setChecked(sharedPrefs.getBoolean(
+ getString(R.string.key_store_media_on_external_storage_pref), false));
}
+ /*
+ * Warning: must call settings.close() after usage!
+ */
+ private static Imps.ProviderSettings.QueryMap getSettings(Context context) {
+ ContentResolver cr = context.getContentResolver();
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,
+ new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},
+ Imps.ProviderSettings.PROVIDER + "=?",
+ new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},
+ null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor,
+ cr,
+ Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS,
+ false /* keep updated */,
+ null /* no handler */);
+ return settings;
+ }
+
+ public static boolean getDeleteUnsecuredMedia(Context context) {
+ Imps.ProviderSettings.QueryMap settings = getSettings(context);
+ boolean value = settings.getDeleteUnsecuredMedia();
+ settings.close();
+ return value;
+ }
+
+
/* save the preferences in Imps so they are accessible everywhere */
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
- getContentResolver(), false /* don't keep updated */, null /* no handler */);
+ ContentResolver cr = getContentResolver();
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, cr,
+ Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, false /* keep updated */, null /* no handler */);
if (key.equals("pref_security_otr_mode")) {
settings.setOtrMode(prefs.getString(key, "auto"));
+ } else if (key.equals("pref_linkify_on_tor")) {
+ settings.setLinkifyOnTor(prefs.getBoolean(key, false));
} else if (key.equals("pref_hide_offline_contacts")) {
settings.setHideOfflineContacts(prefs.getBoolean(key, false));
+ } else if (key.equals("pref_delete_unsecured_media")) {
+ boolean test = prefs.getBoolean(key, false);
+ settings.setDeleteUnsecuredMedia(prefs.getBoolean(key, false));
+ } else if (key.equals("pref_store_media_on_external_storage")) {
+ /* This uses SharedPreferences since it is used before Imps is setup */
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ Editor editor = sharedPrefs.edit();
+ editor.putBoolean(getString(R.string.key_store_media_on_external_storage_pref),
+ prefs.getBoolean(key, false));
+ editor.apply();
} else if (key.equals("pref_enable_notification")) {
settings.setEnableNotification(prefs.getBoolean(key, true));
} else if (key.equals("pref_notification_vibrate")) {
settings.setVibrate(prefs.getBoolean(key, true));
} else if (key.equals("pref_notification_sound")) {
+ /**
// TODO sort out notification sound pref
- if (prefs.getBoolean(key, false)) {
- settings.setRingtoneURI(ProviderSettings.RINGTONE_DEFAULT);
+ if (prefs.getBoolean(key, true)) {
+ settings.setRingtoneURI("android.resource://" + getPackageName() + "/" + R.raw.notify);
} else {
settings.setRingtoneURI(null);
- }
- } else if (key.equals("pref_foreground_service")) {
- settings.setUseForegroundPriority(prefs.getBoolean(key, false));
+ }*/
+ } else if (key.equals("pref_enable_custom_notification")) {
+ /*
+ if (prefs.getBoolean(key, false)) {
+ settings.setRingtoneURI("android.resource://" + getPackageName() + "/" + R.raw.notify);
+ } else {
+ settings.setRingtoneURI(ProviderSettings.RINGTONE_DEFAULT);
+ }*/
+ }
+ else if (key.equals("pref_foreground_enable")) {
+ settings.setUseForegroundPriority(prefs.getBoolean(key, true));
} else if (key.equals("pref_heartbeat_interval")) {
try
{
@@ -102,18 +177,21 @@ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
settings.setHeartbeatInterval((DEFAULT_HEARTBEAT_INTERVAL));
}
}
- else if (key.equals("pref_default_locale"))
+ else if (key.equals("pref_language"))
{
- ((ImApp)getApplication()).setNewLocale(this, prefs.getString(key, ""));
- setResult(2);
-
+ String newLanguage = prefs.getString(key, Languages.USE_SYSTEM_DEFAULT);
+ if (!TextUtils.equals(currentLanguage, newLanguage)) {
+ ((ImApp)getApplication()).setNewLocale(this, newLanguage);
+ setResult(RESULT_OK);
+ finish(); // go to main screen to reset language
+ }
}
else if (key.equals("themeDark"))
{
-
- setResult(2);
+
+ setResult(RESULT_OK);
}
-
+
settings.close();
}
@@ -122,69 +200,134 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
- mHideOfflineContacts = (CheckBoxPreference) findPreference("pref_hide_offline_contacts");
mOtrMode = (ListPreference) findPreference("pref_security_otr_mode");
+ mLanguage = (ListPreference) findPreference("pref_language");
+ mLinkifyOnTor = (CheckBoxPreference) findPreference("pref_linkify_on_tor");
+ mHideOfflineContacts = (CheckBoxPreference) findPreference("pref_hide_offline_contacts");
+ mDeleteUnsecuredMedia = (CheckBoxPreference) findPreference("pref_delete_unsecured_media");
+ mStoreMediaOnExternalStorage = (CheckBoxPreference) findPreference("pref_store_media_on_external_storage");
mEnableNotification = (CheckBoxPreference) findPreference("pref_enable_notification");
mNotificationVibrate = (CheckBoxPreference) findPreference("pref_notification_vibrate");
mNotificationSound = (CheckBoxPreference) findPreference("pref_notification_sound");
- // TODO re-enable Ringtone preference
- //mNotificationRingtone = (CheckBoxPreference) findPreference("pref_notification_ringtone");
- // mForegroundService = (CheckBoxPreference) findPreference("pref_foreground_service");
+
+ mNotificationRingtone = findPreference("pref_notification_ringtone");
+
+ Languages languages = Languages.get(this);
+ currentLanguage = getResources().getConfiguration().locale.getLanguage();
+ mLanguage.setDefaultValue(currentLanguage);
+ mLanguage.setEntries(languages.getAllNames());
+ mLanguage.setEntryValues(languages.getSupportedLocales());
+
+ mNotificationRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener()
+ {
+
+ @Override
+ public boolean onPreferenceClick(Preference arg0) {
+
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getString(R.string.notification_ringtone_title));
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, (Uri) null);
+ startActivityForResult(intent, 5);
+ return true;
+ }
+
+ });
+
+ mForegroundService = (CheckBoxPreference) findPreference("pref_foreground_enable");
mHeartbeatInterval = (EditTextPreference) findPreference("pref_heartbeat_interval");
-
+
mThemeBackground = (EditTextPreference) findPreference("pref_background");
-
+
+
mThemeBackground.setOnPreferenceClickListener(new OnPreferenceClickListener()
{
@Override
public boolean onPreferenceClick(Preference arg0) {
-
+
showThemeChooserDialog ();
return true;
}
-
+
});
}
-
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 888 && data != null && data.getData() != null){
Uri _uri = data.getData();
if (_uri != null) {
- //User had pick an image.
- Cursor cursor = getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
-
- if (cursor != null)
- {
- cursor.moveToFirst();
-
+
//Link to the image
- final String imageFilePath = cursor.getString(0);
- mThemeBackground.setText(imageFilePath);
+ String imageFilePath = getRealPathFromURI(_uri);
+
+ if (imageFilePath != null)
+ mThemeBackground.setText(imageFilePath);
+
mThemeBackground.getDialog().cancel();
- }
+
}
+
+ }
+ else if (resultCode == Activity.RESULT_OK && requestCode == 5)
+ {
+ Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+ ContentResolver cr = getContentResolver();
+ Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null);
+
+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, cr,
+ Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, false /* keep updated */, null /* no handler */);
+
+ if (uri != null)
+ {
+
+ settings.setRingtoneURI(uri.toString());
+
+ }
+ else
+ {
+ settings.setRingtoneURI(null);
+ }
+
+ settings.close();
}
super.onActivityResult(requestCode, resultCode, data);
-
+
}
+
+
+ private String getRealPathFromURI(Uri contentURI) {
+ Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
+ if (cursor == null) { // Source is Dropbox or other similar local file path
+ return contentURI.getPath();
+ } else {
+ cursor.moveToFirst();
+ int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
+ if (idx > -1)
+ return cursor.getString(idx);
+ else
+ return contentURI.toString();
+ }
+ }
+
private void showThemeChooserDialog ()
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Choose Background");
- builder.setMessage("Do you want to select a background image from the Gallery?");
+ builder.setTitle(getString(R.string.dialog_settings_choose_background_title));
+ builder.setMessage(getString(R.string.dialog_settings_choose_background_body));
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
- startActivityForResult(Intent.createChooser(intent, "Select Picture"), 888);
+ startActivityForResult(Intent.createChooser(intent, getString(R.string.dialog_settings_choose_background_picker)), 888);
dialog.dismiss();
}
diff --git a/src/info/guardianproject/otr/app/im/app/SignInHelper.java b/src/info/guardianproject/otr/app/im/app/SignInHelper.java
index 5de2fb8ea..5d55900f5 100644
--- a/src/info/guardianproject/otr/app/im/app/SignInHelper.java
+++ b/src/info/guardianproject/otr/app/im/app/SignInHelper.java
@@ -1,6 +1,5 @@
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.otr.TorProxyInfo;
import info.guardianproject.otr.app.im.IImConnection;
import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.app.adapter.ConnectionListenerAdapter;
@@ -21,19 +20,22 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.widget.TextView;
import android.widget.Toast;
/**
* Handle sign-in process for activities.
- *
+ *
* @author devrandom
- *
+ *
* Users of this helper must call {@link SignInHelper#stop()} to clean up callbacks
* in their onDestroy() or onPause() lifecycle methods.
- *
+ *
*
The helper listens to connection events. It automatically stops listening when the
* connection state is logged-in or disconnected (failed).
*/
@@ -43,15 +45,15 @@ public class SignInHelper {
private ImApp mApp;
private MyConnectionListener mListener;
private Collection connections;
- private Listener mSignInListener;
+ private SignInListener mSignInListener;
// This can be used to be informed of signin events
- public interface Listener {
+ public interface SignInListener {
void connectedToService();
void stateChanged(int state, long accountId);
}
-
- public SignInHelper(Activity context, Listener listener) {
+
+ public SignInHelper(Activity context, SignInListener listener) {
this.mContext = context;
mHandler = new SimpleAlertHandler(context);
mListener = new MyConnectionListener(mHandler);
@@ -60,18 +62,18 @@ public SignInHelper(Activity context, Listener listener) {
mApp = (ImApp)mContext.getApplication();
}
-
+
connections = new HashSet();
}
-
+
public SignInHelper(Activity context) {
this(context, null);
}
-
- public void setSignInListener(Listener listener) {
+
+ public void setSignInListener(SignInListener listener) {
mSignInListener = listener;
}
-
+
public void stop() {
for (IImConnection connection : connections) {
try {
@@ -120,27 +122,31 @@ private void handleConnectionEvent(IImConnection connection, int state, ImErrorI
LogCleaner.error(ImApp.LOG_TAG, "handle connection error",e);
}
}
-
+
if (state == ImConnection.DISCONNECTED) {
// sign in failed
final ProviderDef provider = mApp.getProvider(providerId);
- String providerName = provider.mName;
+ if (provider != null) //a provider might have been deleted
+ {
+ String providerName = provider.mName;
- Resources r = mContext.getResources();
- String errMsg = r.getString(R.string.login_service_failed, providerName, // FIXME
- error == null ? "" : ErrorResUtils.getErrorRes(r, error.getCode()));
-
- Toast.makeText(mContext, errMsg, Toast.LENGTH_LONG).show();
- /*
- new AlertDialog.Builder(mContext).setTitle(R.string.error)
- .setMessage()
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- // FIXME
- }
- }).setCancelable(false).show();
- */
+
+ Resources r = mContext.getResources();
+ String errMsg = r.getString(R.string.login_service_failed, providerName, // FIXME
+ error == null ? "" : ErrorResUtils.getErrorRes(r, error.getCode()));
+
+ // Toast.makeText(mContext, errMsg, Toast.LENGTH_LONG).show();
+ /*
+ new AlertDialog.Builder(mContext).setTitle(R.string.error)
+ .setMessage()
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // FIXME
+ }
+ }).setCancelable(false).show();
+ */
+ }
}
}
@@ -159,34 +165,65 @@ public void signIn(final String password, final long providerId, final long acco
final boolean isActive) {
final ProviderDef provider = mApp.getProvider(providerId);
- final String providerName = provider.mName;
-
- mApp.callWhenServiceConnected(mHandler, new Runnable() {
- public void run() {
- if (mApp.serviceConnected()) {
- if (mSignInListener != null)
- mSignInListener.connectedToService();
- if (!isActive) {
- activateAccount(providerId, accountId);
- }
- signInAccount(password, providerId, providerName, accountId);
+
+ if (provider != null) //provider may be null if deleted, or db not updated yet
+ {
+ final String providerName = provider.mName;
+
+ if (mApp.serviceConnected()) {
+ if (mSignInListener != null)
+ mSignInListener.connectedToService();
+ if (!isActive) {
+ activateAccount(providerId, accountId);
}
+ signInAccount(password, providerId, providerName, accountId);
}
- });
+ else
+ {
+ mApp.callWhenServiceConnected(mHandler, new Runnable() {
+ public void run() {
+ if (mApp.serviceConnected()) {
+ if (mSignInListener != null)
+ mSignInListener.connectedToService();
+ if (!isActive) {
+ activateAccount(providerId, accountId);
+ }
+ signInAccount(password, providerId, providerName, accountId);
+ }
+ }
+ });
+ }
+ }
}
- private void signInAccount(String password, long providerId, String providerName, long accountId) {
+ private void signInAccount(final String password, final long providerId, final String providerName, final long accountId) {
+
+
+ try {
+ signInAccountAsync(password, providerId, providerName, accountId);
+ } catch (RemoteException e) {
+ Log.d(ImApp.LOG_TAG,"error signing in",e);
+ }
+
+
+
+ }
+
+ private void signInAccountAsync(String password, long providerId, String providerName, long accountId) throws RemoteException {
boolean autoLoadContacts = true;
boolean autoRetryLogin = true;
+ IImConnection conn = null;
+
+
+ conn = mApp.getConnection(providerId);
- try {
- IImConnection conn = mApp.getConnection(providerId);
if (conn != null) {
connections.add(conn);
conn.registerConnectionListener(mListener);
int state = conn.getState();
if (mSignInListener != null)
mSignInListener.stateChanged(state, accountId);
+
if (state != ImConnection.DISCONNECTED) {
// already signed in or in the process
if (state == ImConnection.LOGGED_IN) {
@@ -196,6 +233,7 @@ private void signInAccount(String password, long providerId, String providerName
handleConnectionEvent(conn, state, null);
return;
}
+
} else {
conn = mApp.createConnection(providerId, accountId);
if (conn == null) {
@@ -207,46 +245,28 @@ private void signInAccount(String password, long providerId, String providerName
conn.registerConnectionListener(mListener);
}
+ conn.login(password, autoLoadContacts, autoRetryLogin);
+
+ /*
if (mApp.isNetworkAvailableAndConnected()) {
-
- conn.login(password, autoLoadContacts, autoRetryLogin);
+
} else {
- promptForBackgroundDataSetting(providerName);
+ // promptForBackgroundDataSetting(providerName);
return;
- }
- } catch (RemoteException e) {
+ }*/
- mHandler.showServiceErrorAlert(e.getLocalizedMessage());
- LogCleaner.error(ImApp.LOG_TAG, "sign in account",e);
- }
}
- private static final String SYNC_SETTINGS_ACTION = "android.settings.SYNC_SETTINGS";
- private static final String SYNC_SETTINGS_CATEGORY = "android.intent.category.DEFAULT";
-
/**
* Popup a dialog to ask the user whether he/she wants to enable background
* connection to continue. If yes, enable the setting and broadcast the
* change. Otherwise, quit the signing in window immediately.
*/
private void promptForBackgroundDataSetting(String providerName) {
- new AlertDialog.Builder(mContext)
- .setTitle(R.string.bg_data_prompt_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(mContext.getString(R.string.bg_data_prompt_message, providerName))
- .setPositiveButton(R.string.bg_data_prompt_ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- Intent intent = new Intent(SYNC_SETTINGS_ACTION);
- intent.addCategory(SYNC_SETTINGS_CATEGORY);
- mContext.startActivity(intent);
- }
- })
- .setNegativeButton(R.string.bg_data_prompt_cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- }
- }).show();
+
+ Toast.makeText(mContext, mContext.getString(R.string.bg_data_prompt_message, providerName), Toast.LENGTH_LONG).show();
+
+
}
public void activateAccount(long providerId, long accountId) {
diff --git a/src/info/guardianproject/otr/app/im/app/SignoutActivity.java b/src/info/guardianproject/otr/app/im/app/SignoutActivity.java
index 1df221c44..c8255e02b 100644
--- a/src/info/guardianproject/otr/app/im/app/SignoutActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/SignoutActivity.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -51,7 +51,7 @@ protected void onCreate(Bundle icicle) {
}
ContentResolver cr = getContentResolver();
-
+
Cursor c = cr.query(data, ACCOUNT_SELECTION, null /* selection */,
null /* selection args */, null /* sort order */);
final long providerId;
@@ -102,7 +102,7 @@ private void signOut(long providerId, long accountId) {
} finally {
//finish();
- Toast.makeText(this, getString(R.string.signed_out_prompt), Toast.LENGTH_LONG).show();
+ // Toast.makeText(this, getString(R.string.signed_out_prompt), Toast.LENGTH_LONG).show();
}
}
diff --git a/src/info/guardianproject/otr/app/im/app/SimpleAlertHandler.java b/src/info/guardianproject/otr/app/im/app/SimpleAlertHandler.java
index c21b3f40e..29b8acea6 100644
--- a/src/info/guardianproject/otr/app/im/app/SimpleAlertHandler.java
+++ b/src/info/guardianproject/otr/app/im/app/SimpleAlertHandler.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,18 +17,15 @@
package info.guardianproject.otr.app.im.app;
+import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.engine.Contact;
import info.guardianproject.otr.app.im.engine.ContactListListener;
import info.guardianproject.otr.app.im.engine.ImErrorInfo;
-
-import info.guardianproject.otr.app.im.R;
-
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.res.Resources;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
+import android.util.Log;
import android.widget.Toast;
public class SimpleAlertHandler extends Handler {
@@ -46,14 +43,18 @@ protected void promptDisconnectedEvent(Message msg) {
ImApp app = (ImApp)mActivity.getApplication();
ProviderDef provider = app.getProvider(providerId);
ImErrorInfo error = (ImErrorInfo) msg.obj;
- String promptMsg;
- if (error != null) {
+ String promptMsg = null;
+ if (error != null && provider != null) {
promptMsg = mActivity.getString(R.string.signed_out_prompt_with_error, provider.mName,
ErrorResUtils.getErrorRes(mRes, error.getCode()));
- } else {
- promptMsg = mActivity.getString(R.string.signed_out_prompt, provider.mName);
}
- // showAlert(R.string.error, promptMsg); //TODO debug issue
+ else
+ {
+ promptMsg = mActivity.getString(R.string.error);
+ }
+
+ if (promptMsg != null)
+ showAlert(R.string.error, promptMsg);
}
public void registerForBroadcastEvents() {
@@ -83,23 +84,21 @@ public void showAlert(CharSequence title, int messageId) {
}
public void showAlert(final CharSequence title, final CharSequence message) {
- if (Looper.myLooper() == getLooper()) {
- new AlertDialog.Builder(mActivity).setTitle(title).setMessage(message)
- .setPositiveButton(R.string.ok, null).show();
- } else {
- post(new Runnable() {
- public void run() {
- new AlertDialog.Builder(mActivity).setTitle(title).setMessage(message)
- .setPositiveButton(R.string.ok, null).show();
- }
- });
+
+ if (title == null || message == null)
+ return;
+
+ if (!title.equals(message)) //sometimes this reads Attention: Attention!
+ {
+ Toast.makeText(mActivity, title + ": " + message, Toast.LENGTH_SHORT).show();
}
+
}
public void showServiceErrorAlert(String msg) {
showAlert(R.string.error, msg);
}
-
+
public void showContactError(int errorType, ImErrorInfo error, String listName, Contact contact) {
int id = 0;
switch (errorType) {
diff --git a/src/info/guardianproject/otr/app/im/app/SmpResponseActivity.java b/src/info/guardianproject/otr/app/im/app/SmpResponseActivity.java
index c96cacbb9..95c527bed 100644
--- a/src/info/guardianproject/otr/app/im/app/SmpResponseActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/SmpResponseActivity.java
@@ -3,8 +3,9 @@
import info.guardianproject.otr.IOtrChatSession;
import info.guardianproject.otr.OtrDebugLogger;
import info.guardianproject.otr.app.im.IChatSession;
+import info.guardianproject.otr.app.im.R;
+import info.guardianproject.otr.app.im.engine.Address;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@@ -33,9 +34,12 @@ protected void onCreate(Bundle savedInstanceState) {
private void showQuestionDialog() {
- new AlertDialog.Builder(this).setTitle("OTR Verification").setMessage(mQuestion)
+ String title = getString(R.string.smp_question_title);
+ String strQuestion = mSessionId + ": " + mQuestion;
+
+ new AlertDialog.Builder(this).setTitle(title).setMessage(strQuestion)
.setView(mInputSMP)
- .setPositiveButton("Answer", new DialogInterface.OnClickListener() {
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String secret = mInputSMP.getText().toString();
@@ -43,7 +47,7 @@ public void onClick(DialogInterface dialog, int whichButton) {
SmpResponseActivity.this.finish();
}
- }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
@@ -52,14 +56,14 @@ public void onClick(DialogInterface dialog, int whichButton) {
}
private void respondSmp(String sid, String answer) {
-
+
ImApp app = (ImApp)getApplication();
-
+
IOtrChatSession iOtrSession;
try {
- IChatSession chatSession = app.getChatSession(mProviderId, sid);
+ IChatSession chatSession = app.getChatSession(mProviderId, Address.stripResource(sid));
iOtrSession = chatSession.getOtrChatSession();
if (iOtrSession == null) {
OtrDebugLogger.log("no session in progress for provider " + mProviderId);
diff --git a/src/info/guardianproject/otr/app/im/app/ThemeableActivity.java b/src/info/guardianproject/otr/app/im/app/ThemeableActivity.java
index b4149c46c..34a3697f1 100644
--- a/src/info/guardianproject/otr/app/im/app/ThemeableActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/ThemeableActivity.java
@@ -1,85 +1,155 @@
package info.guardianproject.otr.app.im.app;
+import info.guardianproject.otr.app.im.R;
+
import java.io.File;
import android.app.Activity;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBarActivity;
import android.view.Display;
+import android.view.View;
-import com.actionbarsherlock.app.SherlockActivity;
-
-public class ThemeableActivity extends SherlockActivity {
+public class ThemeableActivity extends ActionBarActivity {
private static String mThemeBg = null;
private static Drawable mThemeDrawable = null;
+ protected static boolean mHasBackground = false;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
-
+
((ImApp)this.getApplication()).setAppTheme(this);
-
- setBackgroundImage (this);
-
+
+ mHasBackground = setBackgroundImage (this);
+
super.onCreate(savedInstanceState);
}
- public static void setBackgroundImage (Activity activity)
+ public static boolean setBackgroundImage (Activity activity)
{
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
- //int themeId = settings.getInt("theme", R.style.Theme_Gibberbot_Light);
boolean themeDark = settings.getBoolean("themeDark", false);
String themebg = settings.getString("pref_background", "");
+
+ if (themeDark)
+ {
+
+ if (activity != null)
+ activity.setTheme(R.style.AppThemeDark);
+ }
+ else
+ {
+
+ if (activity != null)
+ activity.setTheme(R.style.AppTheme);
+ }
+
+
+ if (themebg != null && themebg.length() > 0)
+ {
+
+
+ File fileThemeBg = new File(themebg);
+ if (!fileThemeBg.exists())
+ return false;
+
+ if (mThemeBg == null || (!mThemeBg.equals(themebg)))
+ {
+ mThemeBg = themebg;
+
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ int width = display.getWidth(); // deprecated
+ int height = display.getHeight(); // deprecated
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ // Calculate inSampleSize
+ options.inSampleSize = 4;
+
+ Bitmap b = BitmapFactory.decodeFile(themebg, options);
+ if (b == null)
+ return false;
+
+ float ratio = ((float)width)/((float)height);
+ int bgHeight = b.getHeight();
+ int bgWidth = (int)(((float)b.getHeight()) * ratio);
+
+ b = Bitmap.createBitmap(b, 0, 0,Math.min(b.getWidth(),bgWidth),bgHeight);
+
+ mThemeDrawable = new BitmapDrawable(b);
+
+
+ }
+
+ activity.getWindow().setBackgroundDrawable(mThemeDrawable);
+ return true;
+ }
-
+ return false;
+
+ }
+
+ public static void setBackgroundImage (View view, Activity activity)
+ {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
+ boolean themeDark = settings.getBoolean("themeDark", false);
+ String themebg = settings.getString("pref_background", "");
+
+ if(themeDark)
+ view.setBackgroundColor(activity.getResources().getColor(R.color.background_dark));
+ else
+ view.setBackgroundColor(activity.getResources().getColor(R.color.background_light));
+
if (themebg != null && themebg.length() > 0)
{
File fileThemeBg = new File(themebg);
if (!fileThemeBg.exists())
return;
-
+
if (mThemeBg == null || (!mThemeBg.equals(themebg)))
{
mThemeBg = themebg;
-
- Display display = activity.getWindowManager().getDefaultDisplay();
+
+ Display display = activity.getWindowManager().getDefaultDisplay();
int width = display.getWidth(); // deprecated
int height = display.getHeight(); // deprecated
-
+
final BitmapFactory.Options options = new BitmapFactory.Options();
// Calculate inSampleSize
options.inSampleSize = 4;
Bitmap b = BitmapFactory.decodeFile(themebg, options);
-
+ if (b == null)
+ return;
+
float ratio = ((float)width)/((float)height);
int bgHeight = b.getHeight();
int bgWidth = (int)(((float)b.getHeight()) * ratio);
-
+
b = Bitmap.createBitmap(b, 0, 0,Math.min(b.getWidth(),bgWidth),bgHeight);
-
+
mThemeDrawable = new BitmapDrawable(b);
mThemeDrawable.setAlpha(200);
}
-
-
- activity.getWindow().setBackgroundDrawable(mThemeDrawable);
+
+ view.setBackgroundDrawable(mThemeDrawable);
}
-
+
}
-
+
@Override
protected void onResume() {
((ImApp)this.getApplication()).setAppTheme(this);
super.onResume();
}
-
+
}
diff --git a/src/info/guardianproject/otr/app/im/app/UserPresenceView.java b/src/info/guardianproject/otr/app/im/app/UserPresenceView.java
index effa1a1b9..6b18bc583 100644
--- a/src/info/guardianproject/otr/app/im/app/UserPresenceView.java
+++ b/src/info/guardianproject/otr/app/im/app/UserPresenceView.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -76,13 +76,14 @@ protected void onFinishInflate() {
if (isInEditMode())
return;
- mStatusDialogButton = (ImageButton) findViewById(R.id.statusDropDownButton);
+ /**
+ mStatusDialogButton = (ImageButton) findViewById(R.id.statusDropDownButton);
mStatusDialogButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
showStatusListDialog();
}
});
-
+ */
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
}
@@ -115,7 +116,7 @@ private StatusIconAdapter getStatusAdapter() {
}
ImApp app = (ImApp)((Activity)mContext).getApplication();
-
+
BrandingResources brandingRes = app.getBrandingResource(mProviderId);
Drawable icon = brandingRes.getDrawable(PresenceUtils.getStatusIconId(s));
String text = brandingRes.getString(PresenceUtils.getStatusStringRes(s));
@@ -160,7 +161,7 @@ public void setConnection(IImConnection conn) {
private void updateView() {
ImApp app = (ImApp)((Activity)mContext).getApplication();
-
+
BrandingResources brandingRes = app.getBrandingResource(mProviderId);
int status = PresenceUtils.convertStatus(mPresence.getStatus());
mStatusDialogButton.setImageDrawable(brandingRes.getDrawable(PresenceUtils
@@ -189,6 +190,7 @@ private void updateView() {
private TextView initStatusBar(long providerId, boolean showEdit) {
+ /**
EditText statusEdit = (EditText) findViewById(R.id.statusEdit);
statusEdit.setVisibility(View.GONE);
TextView statusView = (TextView) findViewById(R.id.statusView);
@@ -240,6 +242,10 @@ public void onClick(View v) {
return statusView;
}
+ */
+
+ return null;
+
}
void updatePresence(int status, String statusText) {
@@ -253,7 +259,7 @@ void updatePresence(int status, String statusText) {
if (status != -1) {
newPresence.setStatus(status);
}
-
+
if (statusText != null)
newPresence.setStatusText(statusText);
@@ -264,11 +270,11 @@ void updatePresence(int status, String statusText) {
} else {
mPresence = newPresence;
updateView();
-
+
ContentResolver cr = mContext.getContentResolver();
Imps.ProviderSettings.setPresence(cr, mProviderId, status, statusText);
-
-
+
+
}
} catch (RemoteException e) {
// mHandler.showServiceErrorAlert();
@@ -315,9 +321,9 @@ public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
return view;
}
-
+
}
-
+
public void refreshLogginInStatus ()
{
if (mConn != null)
@@ -325,7 +331,7 @@ public void refreshLogginInStatus ()
try {
loggingIn(mConn.getState() == ImConnection.LOGGING_IN);
} catch (RemoteException e) {
-
+
loggingIn(false);
// mHandler.showServiceErrorAlert();
}
@@ -343,6 +349,6 @@ public void loggingIn(boolean loggingIn) {
}
updateView();
}
-
+
}
diff --git a/src/info/guardianproject/otr/app/im/app/WelcomeActivity.java b/src/info/guardianproject/otr/app/im/app/WelcomeActivity.java
index 64346be27..e79214b50 100644
--- a/src/info/guardianproject/otr/app/im/app/WelcomeActivity.java
+++ b/src/info/guardianproject/otr/app/im/app/WelcomeActivity.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2008 The Android Open Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -16,52 +16,53 @@
package info.guardianproject.otr.app.im.app;
-import info.guardianproject.cacheword.CacheWordActivityHandler;
-import info.guardianproject.cacheword.ICacheWordSubscriber;
-import info.guardianproject.cacheword.SQLCipherOpenHelper;
-import info.guardianproject.otr.OtrAndroidKeyManagerImpl;
-import info.guardianproject.otr.app.im.R;
-import info.guardianproject.otr.app.im.engine.ImConnection;
-import info.guardianproject.otr.app.im.provider.Imps;
-import info.guardianproject.otr.app.im.ui.AboutActivity;
-import net.hockeyapp.android.CrashManager;
-import net.hockeyapp.android.UpdateManager;
-import net.sqlcipher.database.SQLiteDatabase;
+import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.app.ProgressDialog;
import android.content.ContentUris;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
+import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
+import android.os.StatFs;
import android.preference.PreferenceManager;
+import android.text.TextUtils;
import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.Toast;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
+import info.guardianproject.cacheword.CacheWordActivityHandler;
+import info.guardianproject.cacheword.CacheWordService;
+import info.guardianproject.cacheword.ICacheWordSubscriber;
+import info.guardianproject.otr.OtrAndroidKeyManagerImpl;
+import info.guardianproject.otr.app.im.IImConnection;
+import info.guardianproject.otr.app.im.R;
+import info.guardianproject.otr.app.im.engine.ImConnection;
+import info.guardianproject.otr.app.im.provider.Imps;
+import info.guardianproject.otr.app.im.provider.SQLCipherOpenHelper;
+
+import java.io.File;
+
+import net.hockeyapp.android.UpdateManager;
public class WelcomeActivity extends ThemeableActivity implements ICacheWordSubscriber {
-
+
private static final String TAG = "WelcomeActivity";
- private boolean mDidAutoLaunch = false;
private Cursor mProviderCursor;
private ImApp mApp;
private SimpleAlertHandler mHandler;
- private String mDefaultLocale;
private SignInHelper mSignInHelper;
private boolean mDoSignIn = true;
-
+
+ private ProgressDialog dialog;
+
static final String[] PROVIDER_PROJECTION = { Imps.Provider._ID, Imps.Provider.NAME,
Imps.Provider.FULLNAME, Imps.Provider.CATEGORY,
Imps.Provider.ACTIVE_ACCOUNT_ID,
@@ -83,82 +84,112 @@ public class WelcomeActivity extends ThemeableActivity implements ICacheWordSubs
static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
static final int ACCOUNT_PRESENCE_STATUS = 9;
static final int ACCOUNT_CONNECTION_STATUS = 10;
-
- private SharedPreferences mPrefs = null;
-
+
private CacheWordActivityHandler mCacheWord = null;
+ private boolean mDoLock;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- SQLiteDatabase.loadLibs(this);
-
+
+ mApp = (ImApp)getApplication();
+ mHandler = new MyHandler(this);
+
mSignInHelper = new SignInHelper(this);
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- mDefaultLocale = mPrefs.getString(getString(R.string.pref_default_locale), null);
-
- this.getSupportActionBar().hide();
-
-
- mDoSignIn = getIntent().getBooleanExtra("doSignIn", true);
-
- checkForCrashes();
-
- checkForUpdates();
-
-
+
+ Intent intent = getIntent();
+ mDoSignIn = intent.getBooleanExtra("doSignIn", true);
+ mDoLock = intent.getBooleanExtra("doLock", false);
+
+ if (!mDoLock)
+ {
+ if (checkMediaStoreFile()) {
+ return;
+ }
+
+ mApp.maybeInit(this);
+ }
+
+ if (ImApp.mUsingCacheword)
+ connectToCacheWord();
+ else
+ {
+ if (openEncryptedStores(null, false)) {
+ try
+ {
+ ChatFileStore.initWithoutPassword(this);
+ }
+ catch (Exception e)
+ {
+ Log.d(ImApp.LOG_TAG,"unable to mount VFS store"); //but let's not crash the whole app right now
+ }
+ } else {
+ connectToCacheWord(); //first time setup
+ }
+ }
+
+ // if we have an incoming contact, send it to the right place
+ String scheme = intent.getScheme();
+ if(TextUtils.equals(scheme, "xmpp"))
+ {
+ intent.setClass(this, AddContactActivity.class);
+ startActivity(intent);
+ finish();
+ return;
+ }
}
-
+
private void connectToCacheWord ()
{
-
- mCacheWord = new CacheWordActivityHandler(this, (ICacheWordSubscriber)this);
mCacheWord = new CacheWordActivityHandler(this, (ICacheWordSubscriber)this);
-
- ((ImApp)getApplication()).setCacheWord(mCacheWord);
-
+
mCacheWord.connectToService();
-
+
+
}
-
-
+
+
@SuppressWarnings("deprecation")
- private boolean cursorUnlocked(String pKey) {
+ private boolean cursorUnlocked(String pKey, boolean allowCreate) {
try {
- mApp = (ImApp)getApplication();
- mHandler = new MyHandler(this);
- ImPluginHelper.getInstance(this).loadAvailablePlugins();
-
Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT;
-
- uri = uri.buildUpon().appendQueryParameter(ImApp.CACHEWORD_PASSWORD_KEY, pKey).build();
-
+
+ Builder builder = uri.buildUpon();
+ if (pKey != null)
+ builder.appendQueryParameter(ImApp.CACHEWORD_PASSWORD_KEY, pKey);
+ if (!allowCreate)
+ builder = builder.appendQueryParameter(ImApp.NO_CREATE_KEY, "1");
+ uri = builder.build();
+
mProviderCursor = managedQuery(uri,
PROVIDER_PROJECTION, Imps.Provider.CATEGORY + "=?" /* selection */,
new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
Imps.Provider.DEFAULT_SORT_ORDER);
-
+
if (mProviderCursor != null)
{
+ ImPluginHelper.getInstance(this).loadAvailablePlugins();
+
mProviderCursor.moveToFirst();
-
+
return true;
}
else
{
return false;
}
-
+
} catch (Exception e) {
- Log.e(ImApp.LOG_TAG, e.getMessage(), e);
-
- Toast.makeText(this, "MAJOR ERROR: Unable to unlock or load app database. Please re-install the app or clear data.",Toast.LENGTH_LONG).show();
- finish();
-
+ // Only complain if we thought this password should succeed
+ if (allowCreate) {
+ Log.e(ImApp.LOG_TAG, e.getMessage(), e);
+
+ Toast.makeText(this, getString(R.string.error_welcome_database), Toast.LENGTH_LONG).show();
+ finish();
+ }
+
// needs to be unlocked
return false;
}
@@ -178,73 +209,43 @@ protected void onPause() {
mHandler.unregisterForBroadcastEvents();
super.onPause();
- mCacheWord.onPause();
+ if (mCacheWord != null)
+ mCacheWord.onPause();
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCacheWord != null)
+ mCacheWord.disconnect();
+ if (dialog != null)
+ dialog.dismiss();
+ }
@Override
protected void onResume() {
super.onResume();
- if (mCacheWord == null)
- connectToCacheWord ();
-
- try
- {
+ if (mCacheWord != null)
mCacheWord.onResume();
- }
- catch (Exception e)
- {
- Log.e("CacheWord","unable to bind to cacheword");
- }
-
- if (!mCacheWord.isLocked())
- {
- String pkey = SQLCipherOpenHelper.encodeRawKey(mCacheWord.getEncryptionKey());
-
- if (pkey != null)
- {
- cursorUnlocked(pkey);
-
- doOnResume();
- }
- }
- else
- {
- showLockScreen();
- }
-
-
}
private void doOnResume() {
-
-
- if (mApp == null) {
-
- mApp = (ImApp)getApplication();
- mHandler = new MyHandler(this);
- ImPluginHelper.getInstance(this).loadAvailablePlugins();
- }
-
-
- mApp.setAppTheme(this);
mHandler.registerForBroadcastEvents();
- int countSignedIn = accountsSignedIn();
int countAvailable = accountsAvailable();
- int countConfigured = accountsConfigured();
-
if (countAvailable == 1) {
// If just one account is available for auto-signin, go there immediately after service starts trying
// to connect.
- mSignInHelper.setSignInListener(new SignInHelper.Listener() {
+ mSignInHelper.setSignInListener(new SignInHelper.SignInListener() {
+ @Override
public void connectedToService() {
}
+ @Override
public void stateChanged(int state, long accountId) {
if (state == ImConnection.LOGGING_IN) {
mSignInHelper.goToAccount(accountId);
@@ -254,116 +255,29 @@ public void stateChanged(int state, long accountId) {
} else {
mSignInHelper.setSignInListener(null);
}
-
-
- if (countSignedIn == 0 && countAvailable > 0 && !mDidAutoLaunch && mDoSignIn) {
- mDidAutoLaunch = true;
- signInAll();
- showAccounts();
- } else if (countSignedIn >= 1) {
- showActiveAccount();
- } else {
- showAccounts();
- }/*
- else
- {
- setContentView(R.layout.welcome_activity);
-
- Button getStarted = ((Button) findViewById(R.id.btnSplashAbout));
-
- getStarted.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- Intent intent = new Intent(getBaseContext(), AboutActivity.class);
- startActivity(intent);
- }
- });
-
-
- }*/
-
- }
-
-
- // Show signed in account
-
- protected boolean showActiveAccount() {
- if (!mProviderCursor.moveToFirst())
- return false;
- do {
- if (!mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN) && isSignedIn(mProviderCursor)) {
- showAccounts();
- return true;
- }
- } while (mProviderCursor.moveToNext());
- return false;
- }
-
-
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
-
- MenuInflater inflater = getSupportMenuInflater();
- inflater.inflate(R.menu.main_list_menu, menu);
-
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_account_settings:
- finish();
- showAccounts();
- return true;
-
- case R.id.menu_about:
- showAbout();
- return true;
-
- case R.id.menu_locale:
- showLocaleDialog();
- return true;
+ Intent intent = getIntent();
+ if (intent != null && intent.getAction() != null && (!intent.getAction().equals(Intent.ACTION_MAIN)))
+ {
+ handleIntentAPILaunch(intent);
}
- return super.onOptionsItemSelected(item);
- }
-
- private void signInAll() {
-
- Log.i(TAG, "signInAll");
- if (!mProviderCursor.moveToFirst())
- return;
-
- do {
- int position = mProviderCursor.getPosition();
- signInAccountAtPosition(position);
-
- } while (mProviderCursor.moveToNext());
-
- }
-
- private boolean signInAccountAtPosition(int position) {
- mProviderCursor.moveToPosition(position);
-
- if (!mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
- int state = mProviderCursor.getInt(ACCOUNT_CONNECTION_STATUS);
- long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
-
- if (state == Imps.ConnectionStatus.OFFLINE) {
- boolean isKeepSignedIn = mProviderCursor.getInt(ACTIVE_ACCOUNT_KEEP_SIGNED_IN) != 0;
- if (isKeepSignedIn) {
- signIn(accountId);
- return true;
- }
-
+ else
+ {
+ if (countAvailable > 0 && mDoSignIn && mProviderCursor.moveToFirst()) {
+ do {
+ if (!mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
+ int state = mProviderCursor.getInt(ACCOUNT_CONNECTION_STATUS);
+ long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
+ if (mProviderCursor.getInt(ACTIVE_ACCOUNT_KEEP_SIGNED_IN) != 0) {
+ signIn(accountId);
+ }
+ }
+ } while (mProviderCursor.moveToNext());
}
+ startActivity(new Intent(getBaseContext(), NewChatActivity.class));
+ finish();
}
-
- return false;
}
private void signIn(long accountId) {
@@ -388,11 +302,6 @@ private void signIn(long accountId) {
boolean isActive = false; // TODO(miron)
mSignInHelper.signIn(password, providerId, accountId, isActive);
}
-
- boolean isSigningIn(Cursor cursor) {
- int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
- return connectionStatus == Imps.ConnectionStatus.CONNECTING;
- }
private boolean isSignedIn(Cursor cursor) {
int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
@@ -400,20 +309,6 @@ private boolean isSignedIn(Cursor cursor) {
return connectionStatus == Imps.ConnectionStatus.ONLINE;
}
- private int accountsSignedIn() {
- if (!mProviderCursor.moveToFirst()) {
- return 0;
- }
- int count = 0;
- do {
- if (isSignedIn(mProviderCursor)) {
- count++;
- }
- } while (mProviderCursor.moveToNext());
-
- return count;
- }
-
private int accountsAvailable() {
if (!mProviderCursor.moveToFirst()) {
return 0;
@@ -430,44 +325,23 @@ private int accountsAvailable() {
return count;
}
- private int accountsConfigured() {
- if (!mProviderCursor.moveToFirst()) {
- return 0;
- }
- int count = 0;
- do {
- if (!mProviderCursor.isNull(ACTIVE_ACCOUNT_USERNAME_COLUMN) &&
- !mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
- count++;
- }
- } while (mProviderCursor.moveToNext());
-
- return count;
- }
+ void handleIntentAPILaunch (Intent srcIntent)
+ {
+ Intent intent = new Intent(this, ImUrlActivity.class);
+ intent.setAction(srcIntent.getAction());
- // private void showAccountSetup() {
- // if (!mProviderCursor.moveToFirst() || mProviderCursor.isNull(ACTIVE_ACCOUNT_ID_COLUMN)) {
- // // add account
- // startActivity(getCreateAccountIntent());
- // } else {
- // // edit existing account
- // startActivity(getEditAccountIntent());
- // }
- // }
-
- private void showAbout() {
- //TODO implement this about form
- Toast.makeText(this, "About Gibberbot\nhttps://guardianproject.info/apps/gibber",
- Toast.LENGTH_LONG).show();
- }
+ if (srcIntent.getData() != null)
+ intent.setData(srcIntent.getData());
-
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (srcIntent.getExtras()!= null)
+ intent.putExtras(srcIntent.getExtras());
+ startActivity(intent);
- void showAccounts() {
- startActivity(new Intent(getBaseContext(), AccountListActivity.class));
+ setIntent(null);
finish();
}
-
+
Intent getEditAccountIntent() {
Intent intent = new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(
Imps.Account.CONTENT_URI, mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN)));
@@ -475,8 +349,8 @@ Intent getEditAccountIntent() {
intent.addCategory(getProviderCategory(mProviderCursor));
return intent;
}
-
-
+
+
private String getProviderCategory(Cursor cursor) {
return cursor.getString(PROVIDER_CATEGORY_COLUMN);
}
@@ -496,88 +370,221 @@ public void handleMessage(Message msg) {
}
}
- private void showLocaleDialog() {
- AlertDialog.Builder ad = new AlertDialog.Builder(this);
- ad.setTitle(getResources().getString(R.string.KEY_PREF_LANGUAGE_TITLE));
-
- Configuration config = getResources().getConfiguration();
- String defaultLangName = config.locale.getDefault().getDisplayName();
- String defaultLangCode = config.locale.getDefault().getCountry();
-
- String[] langs = getResources().getStringArray(R.array.languages);
- langs[0] = langs[0] + " (" + defaultLangName + ")";
-
- ad.setItems(langs,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
-
- String[] locs = getResources().getStringArray(R.array.languages_values);
-
- if (which < locs.length) {
- ((ImApp)getApplication()).setNewLocale(WelcomeActivity.this.getBaseContext(), locs[which]);
-
- Intent intent = getIntent();
- finish();
- startActivity(intent);
- }
- }
- });
-
- ad.show();
- }
-
@Override
public void onCacheWordUninitialized() {
Log.d(ImApp.LOG_TAG,"cache word uninit");
-
- showLockScreen();
+
+ if (mDoLock) {
+ completeShutdown();
+ } else {
+ showLockScreen();
+ }
+ finish();
}
void showLockScreen() {
Intent intent = new Intent(this, LockScreenActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra("originalIntent", getIntent());
+ Intent returnIntent = getIntent();
+ returnIntent.putExtra("doSignIn", mDoSignIn);
+ intent.putExtra("originalIntent", returnIntent);
startActivity(intent);
-
+
}
-
+
@Override
public void onCacheWordLocked() {
-
-
+ if (mDoLock) {
+ Log.d(ImApp.LOG_TAG, "cacheword lock requested but already locked");
+
+ } else {
+ showLockScreen();
+ }
+ finish();
}
@Override
public void onCacheWordOpened() {
- Log.d(ImApp.LOG_TAG,"cache word opened");
-
-
- String pkey = SQLCipherOpenHelper.encodeRawKey(mCacheWord.getEncryptionKey());
-
- if (pkey != null)
- {
-
- cursorUnlocked(pkey);
-
- ((ImApp)getApplication()).initOtrStoreKey();
-
-
- int defaultTimeout = Integer.parseInt(mPrefs.getString("pref_cacheword_timeout",ImApp.DEFAULT_TIMEOUT_CACHEWORD));
- mCacheWord.setTimeoutMinutes(defaultTimeout);
- }
-
-
+ if (mDoLock) {
+ completeShutdown();
+ return;
+ }
+
+ byte[] encryptionKey = mCacheWord.getEncryptionKey();
+ openEncryptedStores(encryptionKey, true);
+
+ ChatFileStore.init(this, mCacheWord.getEncryptionKey());
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static void shutdownAndLock(Activity activity) {
+ ImApp app = (ImApp) activity.getApplication();
+ if (app != null) {
+ for (IImConnection conn : app.getActiveConnections()) {
+ try {
+ conn.logout();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ Intent intent = new Intent(activity, WelcomeActivity.class);
+ // Request lock
+ intent.putExtra("doLock", true);
+ // Clear the backstack
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (Build.VERSION.SDK_INT >= 11)
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ activity.startActivity(intent);
+ activity.finish();
+ }
+
+ private void completeShutdown ()
+ {
+ /* ignore unmount errors and quit ASAP. Threads actively using the VFS will
+ * cause IOCipher's VirtualFileSystem.unmount() to throw an IllegalStateException */
+ try {
+ ChatFileStore.unmount();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ new AsyncTask() {
+
+ @Override
+ protected void onPreExecute() {
+ if (mApp.getActiveConnections().size() > 0)
+ {
+ dialog = new ProgressDialog(WelcomeActivity.this);
+ dialog.setCancelable(true);
+ dialog.setMessage(getString(R.string.signing_out_wait));
+ dialog.show();
+ }
+ }
+
+ @Override
+ protected String doInBackground(String... params) {
+
+ boolean stillConnected = true;
+
+ while (stillConnected)
+ {
+
+ try{
+ IImConnection conn = mApp.getActiveConnections().iterator().next();
+
+ if (conn.getState() == ImConnection.DISCONNECTED || conn.getState() == ImConnection.LOGGING_OUT)
+ {
+ stillConnected = false;
+ }
+ else
+ {
+ conn.logout();
+ stillConnected = true;
+ }
+
+
+ Thread.sleep(500);
+ }catch(Exception e){}
+
+
+ }
+
+ return "";
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ super.onPostExecute(result);
+
+ if (dialog != null)
+ dialog.dismiss();
+
+ mApp.forceStopImService();
+
+ Imps.clearPassphrase(mApp);
+
+ if (mCacheWord != null)
+ {
+ mCacheWord.manuallyLock();
+ }
+
+ Intent cacheWordIntent = CacheWordService
+ .getBlankServiceIntent(getApplicationContext());
+ stopService(cacheWordIntent);
+ finish();
+ }
+ }.execute();
+ }
+
+ private boolean checkMediaStoreFile() {
+ /* First set location based on pref, then override based on where the file is.
+ * This crazy logic is necessary to support old installs that used logic that
+ * is not really predictable, since it was based on whether the SD card was
+ * present or not. */
+ File internalDbFile = new File(ChatFileStore.getInternalDbFilePath(this));
+ boolean internalDbFileUsabe = internalDbFile.isFile() && internalDbFile.canWrite();
+
+ boolean externalDbFileUsable = false;
+ java.io.File externalFilesDir = getExternalFilesDir(null);
+ if (externalFilesDir != null) {
+ File externalDbFile = new File(ChatFileStore.getExternalDbFilePath(this));
+ externalDbFileUsable = externalDbFile.isFile() && externalDbFile.canWrite();
+ }
+ final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean isPrefSet = settings.contains(
+ getString(R.string.key_store_media_on_external_storage_pref));
+ boolean storeMediaOnExternalStorage;
+ if (isPrefSet) {
+ storeMediaOnExternalStorage = settings.getBoolean(
+ getString(R.string.key_store_media_on_external_storage_pref), false);
+ if (storeMediaOnExternalStorage && !externalDbFileUsable) {
+ Intent i = new Intent(this, MissingChatFileStoreActivity.class);
+ startActivity(i);
+ finish();
+ return true;
+ }
+ } else {
+ /* only use external if file already exists only there or internal is almost full */
+ boolean forceExternalStorage = !enoughSpaceInInternalStorage(internalDbFile);
+ if (!internalDbFileUsabe && (externalDbFileUsable || forceExternalStorage)) {
+ storeMediaOnExternalStorage = true;
+ } else {
+ storeMediaOnExternalStorage = false;
+ }
+ Editor editor = settings.edit();
+ editor.putBoolean(getString(R.string.key_store_media_on_external_storage_pref),
+ storeMediaOnExternalStorage);
+ editor.apply();
+ }
+ return false;
+ }
+
+ private static boolean enoughSpaceInInternalStorage(File f) {
+ StatFs stat = new StatFs(f.getParent());
+ long freeSizeInBytes = stat.getAvailableBlocks() * (long) stat.getBlockSize();
+ return freeSizeInBytes > 536870912; // 512 MB
}
-
- private void checkForCrashes() {
- CrashManager.register(this, ImApp.HOCKEY_APP_ID);
- }
+ private boolean openEncryptedStores(byte[] key, boolean allowCreate) {
+ String pkey = (key != null) ? new String(SQLCipherOpenHelper.encodeRawKey(key)) : "";
+ OtrAndroidKeyManagerImpl.setKeyStorePassword(pkey);
+
+ if (cursorUnlocked(pkey, allowCreate)) {
- private void checkForUpdates() {
+ if (mDoLock)
+ completeShutdown();
+ else
+ doOnResume();
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ private void checkForUpdates() {
// Remove this for store builds!
UpdateManager.register(this, ImApp.HOCKEY_APP_ID);
- }
+ }
}
diff --git a/src/info/guardianproject/otr/app/im/app/adapter/ChatListenerAdapter.java b/src/info/guardianproject/otr/app/im/app/adapter/ChatListenerAdapter.java
index 044be6fa3..e1dcfb4d6 100644
--- a/src/info/guardianproject/otr/app/im/app/adapter/ChatListenerAdapter.java
+++ b/src/info/guardianproject/otr/app/im/app/adapter/ChatListenerAdapter.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,15 +17,13 @@
package info.guardianproject.otr.app.im.app.adapter;
+import info.guardianproject.otr.app.im.IChatListener;
+import info.guardianproject.otr.app.im.IChatSession;
import info.guardianproject.otr.app.im.app.ImApp;
import info.guardianproject.otr.app.im.engine.Contact;
import info.guardianproject.otr.app.im.engine.ImErrorInfo;
import info.guardianproject.otr.app.im.engine.Message;
-
-import info.guardianproject.otr.app.im.IChatSession;
-import info.guardianproject.otr.app.im.IChatListener;
import info.guardianproject.util.LogCleaner;
-
import android.os.RemoteException;
import android.util.Log;
@@ -45,10 +43,18 @@ public void onContactLeft(IChatSession ses, Contact contact) {
}
}
- public void onIncomingMessage(IChatSession ses, Message msg) {
+ public boolean onIncomingMessage(IChatSession ses, Message msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
LogCleaner.debug(TAG, "onIncomingMessage(" + ses + ", " + msg + ")");
}
+
+ return true;
+ }
+
+ public void onIncomingData(IChatSession ses, byte[] data) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ LogCleaner.debug(TAG, "onIncomingMessage(" + ses + ", len=" + data.length + ")");
+ }
}
public void onSendMessageError(IChatSession ses, Message msg, ImErrorInfo error) {
@@ -82,4 +88,27 @@ public void onStatusChanged(IChatSession ses) throws RemoteException {
LogCleaner.debug(TAG, "onStatusChanged(" + ses + ")");
}
}
+
+ @Override
+ public void onIncomingFileTransfer(String from, String file) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ LogCleaner.debug(TAG, "onIncomingFileTransfer(" + from + "," + file + ")");
+ }
+ }
+
+ @Override
+ public void onIncomingFileTransferProgress(String file, int percent) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ LogCleaner.debug(TAG, "onIncomingFileTransferProgress(" + file + "," + percent + ")");
+ }
+ }
+
+ @Override
+ public void onIncomingFileTransferError(String file, String message) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ LogCleaner.debug(TAG, "onIncomingFileTransferError(" + file + "," + message + ")");
+ }
+
+ }
+
}
diff --git a/src/info/guardianproject/otr/app/im/app/adapter/ChatSessionListenerAdapter.java b/src/info/guardianproject/otr/app/im/app/adapter/ChatSessionListenerAdapter.java
index 4ed7ca618..6f88bcfbd 100644
--- a/src/info/guardianproject/otr/app/im/app/adapter/ChatSessionListenerAdapter.java
+++ b/src/info/guardianproject/otr/app/im/app/adapter/ChatSessionListenerAdapter.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/adapter/ConnectionListenerAdapter.java b/src/info/guardianproject/otr/app/im/app/adapter/ConnectionListenerAdapter.java
index 07b818df5..41fde00c0 100644
--- a/src/info/guardianproject/otr/app/im/app/adapter/ConnectionListenerAdapter.java
+++ b/src/info/guardianproject/otr/app/im/app/adapter/ConnectionListenerAdapter.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/app/adapter/ContactListListenerAdapter.java b/src/info/guardianproject/otr/app/im/app/adapter/ContactListListenerAdapter.java
index 1421163cb..80470acd5 100644
--- a/src/info/guardianproject/otr/app/im/app/adapter/ContactListListenerAdapter.java
+++ b/src/info/guardianproject/otr/app/im/app/adapter/ContactListListenerAdapter.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/engine/Address.java b/src/info/guardianproject/otr/app/im/engine/Address.java
index 3faf1ffcd..9f0fc100c 100644
--- a/src/info/guardianproject/otr/app/im/engine/Address.java
+++ b/src/info/guardianproject/otr/app/im/engine/Address.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,45 +18,54 @@
package info.guardianproject.otr.app.im.engine;
import android.os.Parcel;
+import android.text.TextUtils;
/**
* An abstract representation of the address to any addressable entities such as
* User, Contact list, User Group, etc.
*/
public abstract class Address {
-
+
/**
* Gets a string representation of this address.
- *
+ *
* @return a string representation of this address.
*/
public abstract String getAddress();
-
+
/**
* Gets a user friendly screen name of this address object.
- *
+ *
* @return the screen name.
*/
- public abstract String getScreenName();
+ public abstract String getUser();
/**
* Gets a resource value
- *
+ *
* @return the resource name.
*/
public abstract String getResource();
-
+ /**
+ * Gets the bare address without any resource
+ * @return the bare address
+ */
+ public abstract String getBareAddress();
+
+
public abstract void writeToParcel(Parcel dest);
public abstract void readFromParcel(Parcel source);
-
+
static public boolean hasResource(String address) {
return address.contains("/");
}
-
+
static public String stripResource(String address) {
- if (address.contains("/"))
+ if (TextUtils.isEmpty(address))
+ return "null";
+ else if (address.contains("/"))
return address.split("/")[0];
else
return address;
diff --git a/src/info/guardianproject/otr/app/im/engine/AddressParcelHelper.java b/src/info/guardianproject/otr/app/im/engine/AddressParcelHelper.java
index 0f7aa50ed..a5b4a0672 100644
--- a/src/info/guardianproject/otr/app/im/engine/AddressParcelHelper.java
+++ b/src/info/guardianproject/otr/app/im/engine/AddressParcelHelper.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
diff --git a/src/info/guardianproject/otr/app/im/engine/ChatGroup.java b/src/info/guardianproject/otr/app/im/engine/ChatGroup.java
index 935f1e7e3..230122741 100644
--- a/src/info/guardianproject/otr/app/im/engine/ChatGroup.java
+++ b/src/info/guardianproject/otr/app/im/engine/ChatGroup.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -36,7 +36,7 @@ public ChatGroup(Address address, String name, ChatGroupManager manager) {
public ChatGroup(Address address, String name, Collection members,
ChatGroupManager manager) {
-
+
mAddress = address;
mName = name;
mManager = manager;
@@ -55,7 +55,7 @@ public Address getAddress() {
/**
* Gets the name of the group.
- *
+ *
* @return the name of the group.
*/
public String getName() {
@@ -72,7 +72,7 @@ public void removeMemberListener(GroupMemberListener listener) {
/**
* Gets an unmodifiable collection of the members of the group.
- *
+ *
* @return an unmodifiable collection of the members of the group.
*/
public List getMembers() {
@@ -81,7 +81,7 @@ public List getMembers() {
/**
* Adds a member to this group. TODO: more docs on async callbacks.
- *
+ *
* @param contact the member to add.
*/
public synchronized void addMemberAsync(Contact contact) {
@@ -90,7 +90,7 @@ public synchronized void addMemberAsync(Contact contact) {
/**
* Removes a member from this group. TODO: more docs on async callbacks.
- *
+ *
* @param contact the member to remove.
*/
public synchronized void removeMemberAsync(Contact contact) {
@@ -99,7 +99,7 @@ public synchronized void removeMemberAsync(Contact contact) {
/**
* Notifies that a contact has joined into this group.
- *
+ *
* @param contact the contact who has joined into the group.
*/
void notifyMemberJoined(Contact contact) {
@@ -111,7 +111,7 @@ void notifyMemberJoined(Contact contact) {
/**
* Notifies that a contact has left this group.
- *
+ *
* @param contact the contact who has left this group.
*/
void notifyMemberLeft(Contact contact) {
@@ -124,7 +124,7 @@ void notifyMemberLeft(Contact contact) {
/**
* Notifies that previous operation on this group has failed.
- *
+ *
* @param error the error information.
*/
void notifyGroupMemberError(ImErrorInfo error) {
@@ -132,4 +132,9 @@ void notifyGroupMemberError(ImErrorInfo error) {
listener.onError(this, error);
}
}
+
+ @Override
+ public boolean isGroup() {
+ return true;
+ }
}
diff --git a/src/info/guardianproject/otr/app/im/engine/ChatGroupManager.java b/src/info/guardianproject/otr/app/im/engine/ChatGroupManager.java
index b061b1a5b..fff871baf 100644
--- a/src/info/guardianproject/otr/app/im/engine/ChatGroupManager.java
+++ b/src/info/guardianproject/otr/app/im/engine/ChatGroupManager.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -27,7 +27,7 @@
* ChatGroupManager manages the creating, removing and the member of ChatGroups.
*/
public abstract class ChatGroupManager {
-
+
protected HashMap mGroups;
protected HashMap mInvitations;
@@ -45,7 +45,7 @@ protected ChatGroupManager() {
/**
* Adds a GroupListener to this manager so that it will be notified when a
* certain group changes.
- *
+ *
* @param listener the listener to be notified.
*/
public void addGroupListener(GroupListener listener) {
@@ -55,7 +55,7 @@ public void addGroupListener(GroupListener listener) {
/**
* Removes a GroupListener from this manager so that it won't be notified
* any more.
- *
+ *
* @param listener the listener to remove.
*/
public void removeGroupListener(GroupListener listener) {
@@ -65,7 +65,7 @@ public void removeGroupListener(GroupListener listener) {
/**
* Sets the InvitationListener to the manager so that it will be notified
* when an invitation from another users received.
- *
+ *
* @param listener the InvitationListener.
*/
public synchronized void setInvitationListener(InvitationListener listener) {
@@ -78,17 +78,17 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* group is created or any error occurs. The newly created group is a
* temporary group and will be automatically deleted when all joined users
* have left.
- *
+ *
* @param name the name of the ChatGroup to be created.
- * @throws Exception
+ * @throws Exception
*/
- public abstract boolean createChatGroupAsync(String name) throws Exception;
+ public abstract boolean createChatGroupAsync(String address, String nickname) throws Exception;
/**
* Deletes a certain ChatGroup. This method returns immediately and the
* registered GroupListeners will be notified when the group is deleted or
* any error occurs. Only the administrator of the ChatGroup can delete it.
- *
+ *
* @param group the ChatGroup to be deleted.
*/
public abstract void deleteChatGroupAsync(ChatGroup group);
@@ -98,7 +98,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* the GroupGroupListeners registered on the group will be notified when the
* member is added or any error occurs. Only the administrator of the
* ChatGroup can add member to it.
- *
+ *
* @param group the ChatGroup to which the member will add.
* @param contact the member to add.
*/
@@ -109,7 +109,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* and the GroupGroupListeners registered on the group will be notified when
* the member is added or any error occurs. Only the administrator of the
* ChatGroup can remove its members.
- *
+ *
* @param group the ChatGroup whose member will be removed.
* @param contact the member to be removed.
*/
@@ -119,7 +119,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* Joins into a certain ChatGroup. This method returns immediately and the
* registered GroupListeners will be notified when the user joined into the
* group or any error occurs.
- *
+ *
* @param address the address of the ChatGroup.
*/
public abstract void joinChatGroupAsync(Address address);
@@ -128,7 +128,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* Leaves a certain ChatGroup.This method returns immediately and the
* registered GroupListeners will be notified when the the user left the
* group or any error occurs.
- *
+ *
* @param group the ChatGroup.
*/
public abstract void leaveChatGroupAsync(ChatGroup group);
@@ -137,7 +137,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
* Invites a user to join a certain ChatGroup. If success, the invitee will
* receive an invitation with information of the group. Otherwise, the
* registered GroupListeners will be notified if any error occurs.
- *
+ *
* @param group the ChatGroup.
* @param invitee the invitee.
*/
@@ -146,7 +146,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
/**
* Accepts an invitation. The user will join the group automatically after
* accept the invitation.
- *
+ *
* @param invitation the invitation to accept.
*/
public abstract void acceptInvitationAsync(Invitation invitation);
@@ -154,7 +154,7 @@ public synchronized void setInvitationListener(InvitationListener listener) {
/**
* Accepts an invitation. The user can only accept or reject the same
* invitation only once.
- *
+ *
* @param inviteId the id of the invitation to accept.
* @see #acceptInvitationAsync(Invitation)
*/
@@ -167,7 +167,7 @@ public void acceptInvitationAsync(String inviteId) {
/**
* Rejects an invitation.
- *
+ *
* @param inviteId the id of the invitation to reject.
* @see #rejectInvitationAsync(Invitation)
*/
@@ -180,24 +180,24 @@ public void rejectInvitationAsync(String inviteId) {
/**
* Rejects an invitation.
- *
+ *
* @param invitation the invitation to reject.
*/
public abstract void rejectInvitationAsync(Invitation invitation);
/**
* Gets a ChatGroup by address.
- *
+ *
* @param address the address of the ChatGroup.
* @return a ChatGroup.
*/
public ChatGroup getChatGroup(Address address) {
- return mGroups.get(address.getAddress());
+ return mGroups.get(address.getBareAddress());
}
/**
* Notifies the GroupListeners that a ChatGroup has changed.
- *
+ *
* @param groupAddress the address of group which has changed.
* @param joined a list of users that have joined the group.
* @param left a list of users that have left the group.
@@ -206,7 +206,7 @@ protected void notifyGroupChanged(Address groupAddress, ArrayList joine
ArrayList left) {
ChatGroup group = mGroups.get(groupAddress.getAddress());
if (group == null) {
- group = new ChatGroup(groupAddress, groupAddress.getScreenName(), this);
+ group = new ChatGroup(groupAddress, groupAddress.getUser(), this);
mGroups.put(groupAddress.getAddress(), group);
}
if (joined != null) {
@@ -244,7 +244,7 @@ protected synchronized void notifyJoinedGroup(ChatGroup group) {
/**
* Notifies the GroupListeners that the user has left a certain group.
- *
+ *
* @param groupAddress the address of the group.
*/
protected synchronized void notifyLeftGroup(ChatGroup group) {
@@ -273,7 +273,7 @@ protected synchronized void notifyGroupInvitation(Invitation invitation) {
/**
* Notifies that a contact has joined into this group.
- *
+ *
* @param group the group into which the contact has joined.
* @param contact the contact who has joined into the group.
*/
@@ -283,7 +283,7 @@ protected void notifyMemberJoined(ChatGroup group, Contact contact) {
/**
* Notifies that a contact has left this group.
- *
+ *
* @param group the group which the contact has left.
* @param contact the contact who has left this group.
*/
@@ -293,10 +293,17 @@ protected void notifyMemberLeft(ChatGroup group, Contact contact) {
/**
* Notifies that previous operation on this group has failed.
- *
+ *
* @param error the error information.
*/
protected void notifyGroupMemberError(ChatGroup group, ImErrorInfo error) {
group.notifyGroupMemberError(error);
}
+
+ /**
+ * ask the server what the default host is for MUC
+ * @return
+ */
+ public abstract String getDefaultMultiUserChatServer ();
+
}
diff --git a/src/info/guardianproject/otr/app/im/engine/ChatSession.java b/src/info/guardianproject/otr/app/im/engine/ChatSession.java
index 3ab432af4..59db2933b 100644
--- a/src/info/guardianproject/otr/app/im/engine/ChatSession.java
+++ b/src/info/guardianproject/otr/app/im/engine/ChatSession.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,12 +18,12 @@
package info.guardianproject.otr.app.im.engine;
import info.guardianproject.otr.OtrChatManager;
+import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress;
import info.guardianproject.otr.app.im.provider.Imps;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
-import java.util.concurrent.CopyOnWriteArrayList;
import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionStatus;
@@ -38,23 +38,22 @@ public class ChatSession {
private ImEntity mParticipant;
private ChatSessionManager mManager;
- private OtrChatManager mOtrChatManager;
+ // private OtrChatManager mOtrChatManager;
- private CopyOnWriteArrayList mListeners;
+ private MessageListener mListener = null;
private Vector mHistoryMessages;
+
/**
* Creates a new ChatSession with a particular participant.
- *
+ *
* @param participant the participant with who the user communicates.
* @param connection the underlying network connection.
*/
ChatSession(ImEntity participant, ChatSessionManager manager) {
mParticipant = participant;
mManager = manager;
- mListeners = new CopyOnWriteArrayList();
mHistoryMessages = new Vector();
-
}
public ImEntity getParticipant() {
@@ -65,40 +64,35 @@ public void setParticipant(ImEntity participant) {
mParticipant = participant;
}
+ /*
public void setOtrChatManager(OtrChatManager otrChatManager) {
mOtrChatManager = otrChatManager;
}
public OtrChatManager getOtrChatManager() {
return mOtrChatManager;
- }
+ }*/
/**
* Adds a MessageListener so that it can be notified of any new message in
* this session.
- *
+ *
* @param listener
*/
- public synchronized void addMessageListener(MessageListener listener) {
- if ((listener != null) && !mListeners.contains(listener)) {
- mListeners.add(listener);
- }
+ public void setMessageListener(MessageListener listener) {
+ mListener = listener;
}
-
- /**
- * Removes a listener from this session.
- *
- * @param listener
- */
- public synchronized void removeMessageListener(MessageListener listener) {
- mListeners.remove(listener);
+
+ public MessageListener getMessageListener ()
+ {
+ return mListener;
}
/**
* Sends a text message to other participant(s) in this session
* asynchronously and adds the message to the history. TODO: more docs on
* async callbacks.
- *
+ *
* @param text the text to send.
*/
// TODO these sendMessageAsync() should probably be renamed to sendMessageAsyncAndLog()/
@@ -111,23 +105,21 @@ public void sendMessageAsync(String text) {
/**
* Sends a message to other participant(s) in this session asynchronously
* and adds the message to the history. TODO: more docs on async callbacks.
- *
+ *
* @param message the message to send.
*/
public int sendMessageAsync(Message message) {
- if (message.getTo() == null)
- message.setTo(mParticipant.getAddress());
-
- // TODO OTRCHAT setFrom here, therefore add the mConnection in ChatSession - ??? NF 8/2013 still needs to be done?
- SessionStatus otrStatus = mOtrChatManager.getSessionStatus(message.getFrom().getAddress(),message.getTo().getAddress());
+ OtrChatManager cm = OtrChatManager.getInstance();
+ SessionID sId = cm.getSessionId(message.getFrom().getAddress(),mParticipant.getAddress().getAddress());
+ SessionStatus otrStatus = cm.getSessionStatus(sId);
+ message.setTo(new XmppAddress(sId.getRemoteUserId()));
+
if (otrStatus == SessionStatus.ENCRYPTED)
{
- SessionID sId = mOtrChatManager.getSessionId(message.getFrom().getAddress(),message.getTo().getAddress());
- boolean verified = mOtrChatManager.getKeyManager().isVerified(sId);
-
-
+ boolean verified = cm.getKeyManager().isVerified(sId);
+
if (verified)
{
message.setType(Imps.MessageType.OUTGOING_ENCRYPTED_VERIFIED);
@@ -136,102 +128,129 @@ public int sendMessageAsync(Message message) {
{
message.setType(Imps.MessageType.OUTGOING_ENCRYPTED);
}
-
- mHistoryMessages.add(message);
-
- mOtrChatManager.transformSending(message);
-
- mManager.sendMessageAsync(this, message);
+
}
else if (otrStatus == SessionStatus.FINISHED)
{
-
- onSendMessageError(message, new ImErrorInfo(ImErrorInfo.INVALID_SESSION_CONTEXT,"Please turn off encryption"));
+ message.setType(Imps.MessageType.POSTPONED);
+ // onSendMessageError(message, new ImErrorInfo(ImErrorInfo.INVALID_SESSION_CONTEXT,"error - session finished"));
+ return message.getType();
}
else
{
- mHistoryMessages.add(message);
-
- mOtrChatManager.transformSending(message);
+ //not encrypted, send to all
+ //message.setTo(new XmppAddress(XmppAddress.stripResource(sId.getRemoteUserId())));
+ message.setType(Imps.MessageType.OUTGOING);
+ }
+ mHistoryMessages.add(message);
+ boolean canSend = cm.transformSending(message);
+
+ if (canSend)
+ {
mManager.sendMessageAsync(this, message);
-
- }
+ }
+ else
+ {
+ //can't be sent due to OTR state
+ message.setType(Imps.MessageType.POSTPONED);
+
+ }
+
return message.getType();
+
+
+
+ }
+
+ /**
+ * Sends message + data to other participant(s) in this session asynchronously.
+ *
+ * @param message the message to send.
+ * @param data the data to send.
+ */
+ public void sendDataAsync(Message message, boolean isResponse, byte[] data) {
+ if (message.getTo() == null)
+ message.setTo(mParticipant.getAddress());
+
+ OtrChatManager cm = OtrChatManager.getInstance();
+
+ cm.transformSending(message, isResponse, data);
+
+ mManager.sendMessageAsync(this, message);
}
/**
* Called by ChatSessionManager when received a message of the ChatSession.
* All the listeners registered in this session will be notified.
- *
+ *
* @param message the received message.
- *
+ *
* @return true if the message was processed correctly, or false
* otherwise (e.g. decryption error)
*/
public boolean onReceiveMessage(Message message) {
mHistoryMessages.add(message);
- SessionStatus otrStatus = mOtrChatManager.getSessionStatus(message.getTo().getAddress(), message.getFrom().getAddress());
+ OtrChatManager cm = OtrChatManager.getInstance();
- SessionID sId = mOtrChatManager.getSessionId(message.getTo().getAddress(),message.getFrom().getAddress());
-
- if (otrStatus == SessionStatus.ENCRYPTED)
+ if (cm != null)
{
- boolean verified = mOtrChatManager.getKeyManager().isVerified(sId);
-
- if (verified)
- {
- message.setType(Imps.MessageType.INCOMING_ENCRYPTED_VERIFIED);
- }
- else
+ SessionStatus otrStatus = cm.getSessionStatus(message.getTo().getAddress(), message.getFrom().getAddress());
+
+ SessionID sId = cm.getSessionId(message.getTo().getAddress(),message.getFrom().getAddress());
+
+ if (otrStatus == SessionStatus.ENCRYPTED)
{
- message.setType(Imps.MessageType.INCOMING_ENCRYPTED);
+ boolean verified = cm.getKeyManager().isVerified(sId);
+
+ if (verified)
+ {
+ message.setType(Imps.MessageType.INCOMING_ENCRYPTED_VERIFIED);
+ }
+ else
+ {
+ message.setType(Imps.MessageType.INCOMING_ENCRYPTED);
+ }
+
}
-
- }
-
-
- // BUG it only seems to find the most recently added listener.
- boolean good = true;
-
- for (MessageListener listener : mListeners) {
- good = good && listener.onIncomingMessage(this, message);
+
}
-
- return good;
+
+ if (mListener != null)
+ mListener.onIncomingMessage(this, message);
+
+ return true;
}
public void onMessageReceipt(String id) {
- for (MessageListener listener : mListeners) {
- listener.onIncomingReceipt(this, id);
- }
+ if (mListener != null)
+ mListener.onIncomingReceipt(this, id);
+
}
public void onMessagePostponed(String id) {
- for (MessageListener listener : mListeners) {
- listener.onMessagePostponed(this, id);
- }
+ if (mListener != null)
+ mListener.onMessagePostponed(this, id);
}
public void onReceiptsExpected() {
- for (MessageListener listener : mListeners) {
- listener.onReceiptsExpected(this);
- }
+ if (mListener != null)
+ mListener.onReceiptsExpected(this);
}
/**
* Called by ChatSessionManager when an error occurs to send a message.
- *
+ *
* @param message
- *
+ *
* @param error the error information.
*/
public void onSendMessageError(Message message, ImErrorInfo error) {
- for (MessageListener listener : mListeners) {
- listener.onSendMessageError(this, message, error);
- }
+ if (mListener != null)
+ mListener.onSendMessageError(this, message, error);
+
}
public void onSendMessageError(String messageId, ImErrorInfo error) {
@@ -246,11 +265,12 @@ public void onSendMessageError(String messageId, ImErrorInfo error) {
/**
* Returns a unmodifiable list of the history messages in this session.
- *
+ *
* @return a unmodifiable list of the history messages in this session.
*/
public List getHistoryMessages() {
return Collections.unmodifiableList(mHistoryMessages);
}
+
}
diff --git a/src/info/guardianproject/otr/app/im/engine/ChatSessionListener.java b/src/info/guardianproject/otr/app/im/engine/ChatSessionListener.java
index 252a5bf98..166dabe17 100644
--- a/src/info/guardianproject/otr/app/im/engine/ChatSessionListener.java
+++ b/src/info/guardianproject/otr/app/im/engine/ChatSessionListener.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -27,7 +27,7 @@ public interface ChatSessionListener {
/**
* Called when a new ChatSession is created.
- *
+ *
* @param session the created ChatSession.
*/
public void onChatSessionCreated(ChatSession session);
diff --git a/src/info/guardianproject/otr/app/im/engine/ChatSessionManager.java b/src/info/guardianproject/otr/app/im/engine/ChatSessionManager.java
index 945e91d82..16c1e552d 100644
--- a/src/info/guardianproject/otr/app/im/engine/ChatSessionManager.java
+++ b/src/info/guardianproject/otr/app/im/engine/ChatSessionManager.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,7 +17,10 @@
package info.guardianproject.otr.app.im.engine;
-import java.util.Vector;
+import info.guardianproject.otr.app.im.service.ChatSessionAdapter;
+import info.guardianproject.otr.app.im.service.ChatSessionManagerAdapter;
+
+import java.util.Hashtable;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -27,22 +30,33 @@
public abstract class ChatSessionManager {
private CopyOnWriteArrayList mListeners;
+ private ChatSessionManagerAdapter mAdapter;
/** Map session to the participant communicate with. */
- protected Vector mSessions;
+ protected Hashtable mSessions;
protected ChatSessionManager() {
mListeners = new CopyOnWriteArrayList();
- mSessions = new Vector();
+ mSessions = new Hashtable();
+ }
+
+ public void setAdapter (ChatSessionManagerAdapter adapter)
+ {
+ mAdapter = adapter;
+ }
+
+ public ChatSessionManagerAdapter getAdapter ()
+ {
+ return mAdapter;
}
/**
* Registers a ChatSessionListener with the ChatSessionManager to receive
* events related to ChatSession.
- *
+ *
* @param listener the listener
*/
- public synchronized void addChatSessionListener(ChatSessionListener listener) {
+ public void addChatSessionListener(ChatSessionListener listener) {
if ((listener != null) && !mListeners.contains(listener)) {
mListeners.add(listener);
}
@@ -50,35 +64,43 @@ public synchronized void addChatSessionListener(ChatSessionListener listener) {
/**
* Removes a ChatSessionListener so that it will no longer be notified.
- *
+ *
* @param listener the listener to remove.
*/
- public synchronized void removeChatSessionListener(ChatSessionListener listener) {
+ public void removeChatSessionListener(ChatSessionListener listener) {
mListeners.remove(listener);
}
/**
* Creates a new ChatSession with specified participant.
- *
+ *
* @param participant the participant.
* @return the created ChatSession.
*/
- public synchronized ChatSession createChatSession(ImEntity participant) {
- for (ChatSession session : mSessions) {
+ public ChatSession createChatSession(ImEntity participant, boolean isNewSession) {
+
+ String sessionKey = Address.stripResource(participant.getAddress().getAddress());
+ ChatSession session = mSessions.get(sessionKey);
+
+ if (session == null)
+ {
+ session = new ChatSession(participant, this);
+ ChatSessionAdapter csa = mAdapter.getChatSessionAdapter(session, isNewSession);
- if (session.getParticipant().equals(participant)) {
- return session;
+
+ mSessions.put(sessionKey,session);
+
+ for (ChatSessionListener listener : mListeners) {
+ listener.onChatSessionCreated(session);
}
- }
-
-
- ChatSession session = new ChatSession(participant, this);
- for (ChatSessionListener listener : mListeners) {
- listener.onChatSessionCreated(session);
}
-
- mSessions.add(session);
+ else
+ {
+ ChatSessionAdapter csa = mAdapter.getChatSessionAdapter(session, isNewSession);
+ session.setMessageListener(csa.getAdaptee().getMessageListener());
+
+ }
return session;
}
@@ -87,17 +109,17 @@ public synchronized ChatSession createChatSession(ImEntity participant) {
* Closes a ChatSession. This only removes the session from the list; the
* protocol implementation should override this if it has special work to
* do.
- *
+ *
* @param session the ChatSession to close.
*/
public void closeChatSession(ChatSession session) {
- mSessions.remove(session);
+ mSessions.remove(session.getParticipant().getAddress().getAddress());
}
/**
* Sends a message to specified participant(s) asynchronously. TODO: more
* docs on async callbacks.
- *
+ *
* @param message the message to send.
*/
public abstract void sendMessageAsync(ChatSession session, Message message);
diff --git a/src/info/guardianproject/otr/app/im/engine/ConnectionFactory.java b/src/info/guardianproject/otr/app/im/engine/ConnectionFactory.java
index 2733e40d6..8e2764f8f 100644
--- a/src/info/guardianproject/otr/app/im/engine/ConnectionFactory.java
+++ b/src/info/guardianproject/otr/app/im/engine/ConnectionFactory.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,6 +18,7 @@
package info.guardianproject.otr.app.im.engine;
// import info.guardianproject.otr.app.im.plugin.loopback.LoopbackConnection;
+import info.guardianproject.otr.app.im.plugin.ImConfigNames;
import info.guardianproject.otr.app.im.plugin.xmpp.LLXmppConnection;
import info.guardianproject.otr.app.im.plugin.xmpp.XmppConnection;
@@ -34,7 +35,7 @@ private ConnectionFactory() {
/**
* Gets the singleton instance of the factory.
- *
+ *
* @return the singleton instance.
*/
public synchronized static ConnectionFactory getInstance() {
@@ -46,14 +47,15 @@ public synchronized static ConnectionFactory getInstance() {
/**
* Creates a new ImConnection.
- *
+ *
* @return the new ImConnection.
* @throws IMException if an error occurs during creating a connection.
*/
- public ImConnection createConnection(Map settings, Context context)
+ public synchronized ImConnection createConnection(Map settings, Context context)
throws ImException {
- if ("XMPP".equals(settings.get("im.protocol"))) {
-
+ String protocolName = settings.get(ImConfigNames.PROTOCOL_NAME);
+ if ("XMPP".equals(protocolName)) {
+
try
{
return new XmppConnection(context);
@@ -63,14 +65,14 @@ public ImConnection createConnection(Map settings, Context conte
throw new ImException(e.getMessage());
}
}
- if ("LLXMPP".equals(settings.get("im.protocol"))) {
+ if ("LLXMPP".equals(protocolName)) {
return new LLXmppConnection(context);
}
- /*else if ("LOOPBACK".equals(settings.get("im.protocol"))) {
+ /*else if ("LOOPBACK".equals(protocolName)) {
return new SMSConnection();
} */
else {
- throw new ImException("Unsupported protocol");
+ throw new ImException("Unsupported protocol: " + protocolName);
}
}
}
diff --git a/src/info/guardianproject/otr/app/im/engine/ConnectionListener.java b/src/info/guardianproject/otr/app/im/engine/ConnectionListener.java
index 6256a49eb..0805a8581 100644
--- a/src/info/guardianproject/otr/app/im/engine/ConnectionListener.java
+++ b/src/info/guardianproject/otr/app/im/engine/ConnectionListener.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -24,7 +24,7 @@
public interface ConnectionListener {
/**
* Called when the connection's state has changed.
- *
+ *
* @param state the new state of the connection.
* @param error the error which caused the state change or null
* it's a normal state change.
diff --git a/src/info/guardianproject/otr/app/im/engine/Contact.java b/src/info/guardianproject/otr/app/im/engine/Contact.java
index be69b444c..498404130 100644
--- a/src/info/guardianproject/otr/app/im/engine/Contact.java
+++ b/src/info/guardianproject/otr/app/im/engine/Contact.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,10 +17,12 @@
package info.guardianproject.otr.app.im.engine;
+import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress;
import android.os.Parcel;
import android.os.Parcelable;
public class Contact extends ImEntity implements Parcelable {
+
private Address mAddress;
private String mName;
private Presence mPresence;
@@ -46,13 +48,17 @@ public String getName() {
return mName;
}
+ public void setName( String aName ) {
+ mName = aName;
+ }
+
public Presence getPresence() {
return mPresence;
}
public boolean equals(Object other) {
-
- return other instanceof Contact && mAddress.getAddress().equals(((Contact) other).mAddress.getAddress());
+
+ return other instanceof Contact && mAddress.getBareAddress().equals(((Contact) other).getAddress().getBareAddress());
}
public int hashCode() {
@@ -61,11 +67,13 @@ public int hashCode() {
/* Set the presence of the Contact. Note that this method is public but not
* provide to the user.
- *
+ *
* @param presence the new presence
*/
public void setPresence(Presence presence) {
mPresence = presence;
+ if (mPresence != null && mPresence.getResource() != null)
+ mAddress = new XmppAddress(mAddress.getBareAddress() + '/' + mPresence.getResource());
}
public void writeToParcel(Parcel dest, int flags) {
diff --git a/src/info/guardianproject/otr/app/im/engine/ContactList.java b/src/info/guardianproject/otr/app/im/engine/ContactList.java
index af90c3d73..eb6baaa7d 100644
--- a/src/info/guardianproject/otr/app/im/engine/ContactList.java
+++ b/src/info/guardianproject/otr/app/im/engine/ContactList.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,11 +17,14 @@
package info.guardianproject.otr.app.im.engine;
+import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
public class ContactList extends ImEntity {
+
protected Address mAddress;
protected String mName;
protected boolean mDefault;
@@ -39,7 +42,9 @@ public ContactList(Address address, String name, boolean isDefault,
mContactsCache = new HashMap();
if (contacts != null) {
for (Contact c : contacts) {
- mContactsCache.put(manager.normalizeAddress(c.getAddress().getAddress()), c);
+ String aKey = mManager.normalizeAddress(address.getAddress());
+ if (!mContactsCache.containsKey(aKey))
+ mContactsCache.put(aKey, c);
}
}
}
@@ -72,14 +77,13 @@ public boolean isDefault() {
/**
* Add a contact to the list. The contact is specified by its address
* string.
- *
+ *
* @param address the address string specifies the contact.
* @throws IllegalArgumentException if the address is invalid.
* @throws NullPointerException if the address string is null
* @throws ImException if the contact is not allowed to be added
*/
- public void addContact(String address) throws ImException {
- address = mManager.normalizeAddress(address);
+ public void addContact(final String address) throws ImException {
if (null == address) {
throw new NullPointerException();
@@ -92,19 +96,61 @@ public void addContact(String address) throws ImException {
}
}
- if (containsContact(address)) {
+ new Thread ()
+ {
+
+ public void run ()
+ {
+ Contact contact = getContact(address);
+
+ if (contact == null)
+ {
+ contact = new Contact (new XmppAddress(address),address);
+ }
+
+ try {
+ mManager.addContactToListAsync(contact, ContactList.this);
+ } catch (ImException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Add a contact to the list. The contact is specified by its address
+ * string.
+ *
+ * @param address the address string specifies the contact.
+ * @throws IllegalArgumentException if the address is invalid.
+ * @throws NullPointerException if the address string is null
+ * @throws ImException if the contact is not allowed to be added
+ */
+ public void addExistingContact(Contact contact) throws ImException {
+
+ String aKey = mManager.normalizeAddress(contact.getAddress().getAddress());
+
+ if (mManager.getState() == ContactListManager.BLOCKED_LIST_LOADED) {
+ if (mManager.isBlocked(aKey)) {
+ throw new ImException(ImErrorInfo.CANT_ADD_BLOCKED_CONTACT,
+ "Contact has been blocked");
+ }
+ }
+
+ if (containsContact(aKey)) {
throw new ImException(ImErrorInfo.CONTACT_EXISTS_IN_LIST,
"Contact already exists in the list");
}
- mManager.addContactToListAsync(address, this);
+ mContactsCache.put(aKey, contact);
}
/**
* Remove a contact from the list. If the contact is not in the list,
* nothing will happen. Otherwise, the contact will be removed from the list
* on the server asynchronously.
- *
+ *
* @param address the address of the contact to be removed from the list
* @throws NullPointerException If the address is null
*/
@@ -122,7 +168,7 @@ public void removeContact(Address address) throws ImException {
* Remove a contact from the list. If the contact is not in the list,
* nothing will happen. Otherwise, the contact will be removed from the list
* on the server asynchronously.
- *
+ *
* @param contact the contact to be removed from the list
* @throws NullPointerException If the contact is null
*/
@@ -133,30 +179,31 @@ public void removeContact(Contact contact) throws ImException {
if (containsContact(contact)) {
mManager.removeContactFromListAsync(contact, this);
+ mContactsCache.remove(mManager.normalizeAddress(contact.getAddress().getAddress()));
}
}
- public synchronized Contact getContact(Address address) {
- return mContactsCache.get(mManager.normalizeAddress(address.getAddress()));
+ public Contact getContact(Address address) {
+ return getContact(mManager.normalizeAddress(address.getAddress()));
}
- public synchronized Contact getContact(String address) {
- return mContactsCache.get(mManager.normalizeAddress(address));
+ public Contact getContact(String address) {
+ return mContactsCache.get(address);
}
- public synchronized int getContactsCount() {
+ public int getContactsCount() {
return mContactsCache.size();
}
- public synchronized Collection getContacts() {
+ public Collection getContacts() {
return new ArrayList(mContactsCache.values());
}
- public synchronized boolean containsContact(String address) {
+ public boolean containsContact(String address) {
return mContactsCache.containsKey(mManager.normalizeAddress(address));
}
- public synchronized boolean containsContact(Address address) {
+ public boolean containsContact(Address address) {
return address == null ? false : mContactsCache.containsKey(mManager
.normalizeAddress(address.getAddress()));
}
diff --git a/src/info/guardianproject/otr/app/im/engine/ContactListListener.java b/src/info/guardianproject/otr/app/im/engine/ContactListListener.java
index 9effa3763..65238ba71 100644
--- a/src/info/guardianproject/otr/app/im/engine/ContactListListener.java
+++ b/src/info/guardianproject/otr/app/im/engine/ContactListListener.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007 Esmertec AG. Copyright (C) 2007 The Android Open Source
* Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -49,7 +49,7 @@ public interface ContactListListener {
* Called when: - a contact list has been created, deleted, renamed
* or loaded, or
- a contact has been added to or removed from a list, or
*
- a contact has been blocked or unblocked
- *
+ *
* @param type one of the following values: - {@link #LIST_CREATED}
* list: the newly created list; contact: null
-
* {@link #LIST_DELETED} list: the delete list; contact: null
@@ -76,7 +76,7 @@ public interface ContactListListener {
/**
* Called when received one or more contacts' updated presence information
* from the server.
- *
+ *
* @param contacts one or more contacts that have updated presence
* information.
*/
diff --git a/src/info/guardianproject/otr/app/im/engine/ContactListManager.java b/src/info/guardianproject/otr/app/im/engine/ContactListManager.java
index 0e1a4bb1c..a2a7cb179 100644
--- a/src/info/guardianproject/otr/app/im/engine/ContactListManager.java
+++ b/src/info/guardianproject/otr/app/im/engine/ContactListManager.java
@@ -1,13 +1,13 @@
/*
* Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open
* Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -17,6 +17,8 @@
package info.guardianproject.otr.app.im.engine;
+import info.guardianproject.otr.app.im.ISubscriptionListener;
+
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -52,7 +54,7 @@ public abstract class ContactListManager {
protected Vector