/* * 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 org.awesomeapp.messenger.ui; import android.Manifest; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.widget.SimpleCursorAdapter; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import org.awesomeapp.messenger.ImApp; import org.awesomeapp.messenger.crypto.otr.OtrAndroidKeyManagerImpl; import org.awesomeapp.messenger.model.ImConnection; import org.awesomeapp.messenger.plugin.xmpp.XmppAddress; import org.awesomeapp.messenger.provider.Imps; import org.awesomeapp.messenger.service.IContactList; import org.awesomeapp.messenger.service.IContactListManager; import org.awesomeapp.messenger.service.IImConnection; import org.awesomeapp.messenger.tasks.AddContactAsyncTask; import org.awesomeapp.messenger.ui.legacy.SimpleAlertHandler; import org.awesomeapp.messenger.ui.onboarding.OnboardingManager; import org.awesomeapp.messenger.util.XmppUriHelper; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import im.zom.messenger.R; public class AddContactActivity extends BaseActivity { private static final String TAG = "AddContactActivity"; private static final String[] CONTACT_LIST_PROJECTION = { Imps.ContactList._ID, Imps.ContactList.NAME, }; private static final int CONTACT_LIST_NAME_COLUMN = 1; private EditText mNewAddress; //private Spinner mListSpinner; // Button mInviteButton; ImApp mApp; SimpleAlertHandler mHandler; private Cursor mCursorProviders; private long mProviderId, mAccountId; private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setTitle(""); mApp = (ImApp)getApplication(); mHandler = new SimpleAlertHandler(this); setContentView(R.layout.add_contact_activity); TextView label = (TextView) findViewById(R.id.input_contact_label); mNewAddress = (EditText) findViewById(R.id.email); mNewAddress.addTextChangedListener(mTextWatcher); mNewAddress.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { if (actionId == EditorInfo.IME_ACTION_SEND) { inviteBuddies(); } return false; } }); Intent intent = getIntent(); String scheme = intent.getScheme(); if (TextUtils.equals(scheme, "xmpp")) { addContactFromUri(intent.getData()); } setupActions (); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @Override protected void onResume() { super.onResume(); if (!checkConnection()) { Snackbar sb = Snackbar.make(findViewById(R.id.main_content), R.string.error_suspended_connection, Snackbar.LENGTH_LONG); sb.setAction(getString(R.string.connect), new View.OnClickListener() { @Override public void onClick(View view) { Intent i = new Intent(AddContactActivity.this, AccountsActivity.class); startActivity(i); } }); sb.show(); } } private void setupActions () { View btnInviteSms = findViewById(R.id.btnInviteSMS); btnInviteSms.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ImApp app = ((ImApp)getApplication()); String nickname = app.getDefaultNickname(); if (nickname == null) nickname = new XmppAddress(app.getDefaultUsername()).getUser(); String inviteString = OnboardingManager.generateInviteMessage(AddContactActivity.this, nickname, app.getDefaultUsername(), app.getDefaultOtrKey()); OnboardingManager.inviteSMSContact(AddContactActivity.this, null, inviteString); } }); View btnInviteShare = findViewById(R.id.btnInviteShare); btnInviteShare.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ImApp app = ((ImApp)getApplication()); String nickname = app.getDefaultNickname(); if (nickname == null) nickname = new XmppAddress(app.getDefaultUsername()).getUser(); String inviteString = OnboardingManager.generateInviteMessage(AddContactActivity.this, nickname, app.getDefaultUsername(), app.getDefaultOtrKey()); OnboardingManager.inviteShare(AddContactActivity.this, inviteString); } }); View btnInviteQR = findViewById(R.id.btnInviteScan); btnInviteQR.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (hasCameraPermission()) { ImApp app = ((ImApp) getApplication()); String nickname = app.getDefaultNickname(); if (nickname == null) nickname = new XmppAddress(app.getDefaultUsername()).getUser(); String inviteString; try { inviteString = OnboardingManager.generateInviteLink(AddContactActivity.this, app.getDefaultUsername(), app.getDefaultOtrKey(), nickname); OnboardingManager.inviteScan(AddContactActivity.this, inviteString); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); } private final static int MY_PERMISSIONS_REQUEST_CAMERA = 1; boolean hasCameraPermission () { int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if (permissionCheck == PackageManager.PERMISSION_DENIED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_PERMISSIONS_REQUEST_CAMERA); return false; } else { return true; } } @Override protected void onDestroy() { super.onDestroy(); if (mCursorProviders != null && (!mCursorProviders.isClosed())) mCursorProviders.close(); } private void setupAccountSpinner () { final Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT; mCursorProviders = 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_item, mCursorProviders, new String[] { Imps.Provider.ACTIVE_ACCOUNT_USERNAME }, new int[] { android.R.id.text1 }); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // TODO Something is causing the managedQuery() to return null, use null guard for now if (mCursorProviders != null && mCursorProviders.getCount() > 0) { mCursorProviders.moveToFirst(); mProviderId = mCursorProviders.getLong(PROVIDER_ID_COLUMN); mAccountId = mCursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN); } /** mListSpinner.setAdapter(adapter); mListSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if (mCursorProviders == null) return; mCursorProviders.moveToPosition(arg2); mProviderId = mCursorProviders.getLong(PROVIDER_ID_COLUMN); mAccountId = mCursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN); } @Override public void onNothingSelected(AdapterView<?> arg0) { // TODO Auto-generated method stub } }); */ } public class ProviderListItemFactory implements LayoutInflater.Factory { @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if (name != null && name.equals(AccountListItem.class.getName())) { // return new ProviderListItem(context, AddContactActivity.this, null); return new AccountListItem(context, attrs); } return null; } } private int searchInitListPos(Cursor c, String listName) { if (TextUtils.isEmpty(listName)) { return 0; } c.moveToPosition(-1); while (c.moveToNext()) { if (listName.equals(c.getString(CONTACT_LIST_NAME_COLUMN))) { return c.getPosition(); } } return 0; } private String getDomain (long providerId) { //mDefaultDomain = Imps.ProviderSettings.getStringValue(getContentResolver(), mProviderId, // ImpsConfigNames.DEFAULT_DOMAIN); 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(providerId)}, null); Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap( pCursor, cr, providerId, false /* don't keep updated */, null /* no handler */); String domain = settings.getDomain();//get domain of current user settings.close(); pCursor.close(); return domain; } void inviteBuddies() { Rfc822Token[] recipients = Rfc822Tokenizer.tokenize(mNewAddress.getText()); Pattern pattern = Pattern.compile(EMAIL_PATTERN); boolean foundOne = false; for (Rfc822Token recipient : recipients) { String address = recipient.getAddress(); if (pattern.matcher(address).matches()) { new AddContactAsyncTask(mApp.getDefaultProviderId(), mApp.getDefaultAccountId(), mApp).execute(address, null, null); foundOne = true; } } if (foundOne) { Intent intent = new Intent(); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME, recipients[0].getAddress()); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_PROVIDER, mApp.getDefaultProviderId()); setResult(RESULT_OK, intent); finish(); } } private IContactList getContactList(IImConnection conn) { if (conn == null) { return null; } try { IContactListManager contactListMgr = conn.getContactListManager(); String listName = "";//getSelectedListName(); if (!TextUtils.isEmpty(listName)) { return contactListMgr.getContactList(listName); } else { // Use the default list List<IBinder> lists = contactListMgr.getContactLists(); for (IBinder binder : lists) { IContactList list = IContactList.Stub.asInterface(binder); if (list.isDefault()) { return list; } } // No default list, use the first one as default list if (!lists.isEmpty()) { return IContactList.Stub.asInterface(lists.get(0)); } return null; } } catch (RemoteException e) { // If the service has died, there is no list for now. return null; } } /** private String getSelectedListName() { Cursor c = (Cursor) mListSpinner.getSelectedItem(); return (c == null) ? null : c.getString(CONTACT_LIST_NAME_COLUMN); }*/ private View.OnClickListener mButtonHandler = new View.OnClickListener() { public void onClick(View v) { mApp.callWhenServiceConnected(mHandler, new Runnable() { public void run() { inviteBuddies(); } }); } }; private View.OnClickListener mScanHandler = new View.OnClickListener() { public void onClick(View v) { // new IntentIntegrator(AddContactActivity.this).initiateScan(); } }; private TextWatcher mTextWatcher = new TextWatcher() { public void afterTextChanged(Editable s) { } public void beforeTextChanged(CharSequence s, int start, int count, int after) { // noop } public void onTextChanged(CharSequence s, int start, int before, int count) { // noop } }; private static void log(String msg) { Log.d(ImApp.LOG_TAG, "<AddContactActivity> " + msg); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) { if (resultCode == RESULT_OK) { if (requestCode == OnboardingManager.REQUEST_SCAN) { ArrayList<String> resultScans = resultIntent.getStringArrayListExtra("result"); for (String resultScan : resultScans) { try { if (resultScan.startsWith("xmpp:")) { String address = XmppUriHelper.parse(Uri.parse(resultScan)).get(XmppUriHelper.KEY_ADDRESS); String fingerprint = XmppUriHelper.getOtrFingerprint(resultScan); new AddContactAsyncTask(mApp.getDefaultProviderId(), mApp.getDefaultAccountId(), mApp).execute(address, fingerprint); Intent intent=new Intent(); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME, address); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_PROVIDER, mApp.getDefaultProviderId()); setResult(RESULT_OK, intent); } else { //parse each string and if they are for a new user then add the user String[] parts = OnboardingManager.decodeInviteLink(resultScan); String address = parts[0]; String fingerprint = null, nickname = null; if (parts.length > 1) fingerprint = parts[1]; if (parts.length > 2) nickname = parts[2]; new AddContactAsyncTask(mApp.getDefaultProviderId(), mApp.getDefaultAccountId(), mApp).execute(address, fingerprint, nickname); Intent intent=new Intent(); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME, address); intent.putExtra(ContactsPickerActivity.EXTRA_RESULT_PROVIDER, mApp.getDefaultProviderId()); setResult(RESULT_OK, intent); } //if they are for a group chat, then add the group } catch (Exception e) { Log.w(ImApp.LOG_TAG, "error parsing QR invite link", e); } } } finish(); } } /** * Implement {@code xmpp:} URI parsing according to the RFC: http://tools.ietf.org/html/rfc5122 * @param uri the URI to be parsed */ private void addContactFromUri(Uri uri) { Log.i(TAG, "addContactFromUri: " + uri + " scheme: " + uri.getScheme()); Map<String, String> parsedUri = XmppUriHelper.parse(uri); if (!parsedUri.containsKey(XmppUriHelper.KEY_ADDRESS)) { Toast.makeText(this, "error parsing address: " + uri, Toast.LENGTH_LONG).show(); return; } String address = parsedUri.get(XmppUriHelper.KEY_ADDRESS); mNewAddress.setText(address); // this.mInviteButton.setBackgroundColor(R.drawable.btn_green); //store this for future use... ideally the user comes up as verified the first time! String fingerprint = parsedUri.get(XmppUriHelper.KEY_OTR_FINGERPRINT); if (!TextUtils.isEmpty(fingerprint)) { Log.i(TAG, "fingerprint: " + fingerprint); OtrAndroidKeyManagerImpl.getInstance(this).verifyUser(address, fingerprint); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } private boolean checkConnection() { try { if (mApp.getDefaultProviderId() != -1) { IImConnection conn = mApp.getConnection(mApp.getDefaultProviderId(), mApp.getDefaultAccountId()); if (conn.getState() == ImConnection.DISCONNECTED || conn.getState() == ImConnection.SUSPENDED || conn.getState() == ImConnection.SUSPENDING) return false; } return true; } catch (Exception e) { return false; } } 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; }