multi-device: add account creation/login
Tuleap: #953
Change-Id: I2599f5b8505609a556556b9fa6ca8ced3783cd3c
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
index 12f6667..0c6d476 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
@@ -89,7 +89,7 @@
/*
entryView.alias.setText(accounts.get(pos).getAlias());
- entryView.host.setText(accounts.get(pos).getHost() + " - " + accounts.get(pos).getRegistered_state());
+ entryView.host.setText(accounts.get(pos).getHost() + " - " + accounts.get(pos).getRegistrationState());
// accManager.displayAccountDetails(accounts.get(pos), entryView);
entryView.error.setVisibility(View.GONE);
*/
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
index 38f63ca..b7d3e23 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
+import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
@@ -49,6 +50,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -56,12 +60,19 @@
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
import java.util.HashMap;
import cx.ring.R;
+import cx.ring.model.account.Account;
import cx.ring.model.account.AccountDetail;
import cx.ring.model.account.AccountDetailAdvanced;
import cx.ring.model.account.AccountDetailBasic;
+import cx.ring.model.account.AccountDetailVolatile;
import cx.ring.service.LocalService;
public class AccountCreationFragment extends Fragment {
@@ -79,10 +90,18 @@
// UI references.
private LinearLayout mSipFormLinearLayout;
+ private LinearLayout mAddAccountLayout;
+ private LinearLayout mNewAccountLayout;
+
private EditText mAliasView;
private EditText mHostnameView;
private EditText mUsernameView;
private EditText mPasswordView;
+ //private EditText mRingUsername;
+ private EditText mRingPassword;
+ private EditText mRingPasswordRepeat;
+ private EditText mRingPin;
+ private EditText mRingAddPassword;
private String mDataPath;
@@ -94,6 +113,22 @@
super.onCreate(savedInstanceState);
}
+ private void flipForm(boolean addacc, boolean newacc) {
+ mAddAccountLayout.setVisibility(addacc ? View.VISIBLE : View.GONE);
+ mNewAccountLayout.setVisibility(newacc ? View.VISIBLE : View.GONE);
+ InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (newacc) {
+ mRingPassword.requestFocus();
+ imm.showSoftInput(mRingPassword, InputMethodManager.SHOW_IMPLICIT);
+ } else if (addacc) {
+ mRingPin.requestFocus();
+ imm.showSoftInput(mRingPin, InputMethodManager.SHOW_IMPLICIT);
+ }
+ if (addacc || newacc) {
+ mSipFormLinearLayout.setVisibility(View.GONE);
+ }
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
final View inflatedView = inflater.inflate(R.layout.frag_account_creation, parent, false);
@@ -102,7 +137,111 @@
mHostnameView = (EditText) inflatedView.findViewById(R.id.hostname);
mUsernameView = (EditText) inflatedView.findViewById(R.id.username);
mPasswordView = (EditText) inflatedView.findViewById(R.id.password);
+ //mRingUsername = (EditText) inflatedView.findViewById(R.id.ring_alias);
+ mRingPassword = (EditText) inflatedView.findViewById(R.id.ring_password);
+ mRingPasswordRepeat = (EditText) inflatedView.findViewById(R.id.ring_password_repeat);
+ mRingPin = (EditText) inflatedView.findViewById(R.id.ring_add_pin);
+ mRingAddPassword = (EditText) inflatedView.findViewById(R.id.ring_add_password);
+ final Button ring_create_btn = (Button) inflatedView.findViewById(R.id.ring_create_btn);
+ final Button ring_add_btn = (Button) inflatedView.findViewById(R.id.ring_add_account);
+
+ /*mRingUsername.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence name, int i, int i1, int i2) {
+ LocalService service = mCallbacks.getService();
+ if (service == null)
+ return;
+ service.getNameDirectory().findAddr(name.toString(), new LocalService.NameRequest() {
+ @Override
+ public void onResult(String res, Object err) {
+ Log.w(TAG, "mRingUsername onResult " + res + " " + err);
+ if (err == null && res != null && !res.isEmpty()) {
+ mRingUsername.setError("Username already taken");
+ } else {
+ mRingUsername.setError(null);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {}
+ });*/
+ mRingPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_NEXT)
+ return checkPassword(v, null);
+ return false;
+ }
+ });
+ mRingPassword.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ checkPassword((TextView) v, null);
+ } else {
+ //alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ }
+ }
+ });
+ mRingPasswordRepeat.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ if (mRingPassword.getText().length() != 0 && !checkPassword(mRingPassword, v)) {
+ ring_create_btn.callOnClick();
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ mRingAddPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ ring_add_btn.callOnClick();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ ring_create_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mNewAccountLayout.getVisibility() == View.GONE) {
+ flipForm(false, true);
+ } else {
+ if (!checkPassword(mRingPassword, mRingPasswordRepeat)) {
+ mAccountType = AccountDetailBasic.ACCOUNT_TYPE_RING;
+ //mAlias = mRingUsername.getText().toString();
+ mUsername = mAlias;
+ initAccountCreation(/*mRingUsername.getText().toString()*/null, null, mRingPassword.getText().toString());
+ }
+ }
+ }
+ });
+ ring_add_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mAddAccountLayout.getVisibility() == View.GONE) {
+ flipForm(true, false);
+ } else if (mRingPin.getText().length() != 0 && mRingAddPassword.getText().length() != 0) {
+ mAccountType = AccountDetailBasic.ACCOUNT_TYPE_RING;
+ mUsername = mAlias;
+ initAccountCreation(null, mRingPin.getText().toString(), mRingAddPassword.getText().toString());
+ }
+ }
+ });
mPasswordView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@@ -114,13 +253,13 @@
return true;
}
});
- inflatedView.findViewById(R.id.ring_card_view).setOnClickListener(new View.OnClickListener() {
+ /*inflatedView.findViewById(R.id.ring_card_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mAccountType = AccountDetailBasic.ACCOUNT_TYPE_RING;
initAccountCreation();
}
- });
+ });*/
inflatedView.findViewById(R.id.create_sip_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -132,13 +271,15 @@
attemptCreation();
}
});
- inflatedView.findViewById(R.id.import_card_view).setOnClickListener(new View.OnClickListener() {
+ /*inflatedView.findViewById(R.id.import_card_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startImport();
}
- });
+ });*/
+ mNewAccountLayout = (LinearLayout) inflatedView.findViewById(R.id.newAccountLayout);
+ mAddAccountLayout = (LinearLayout) inflatedView.findViewById(R.id.addAccountLayout);
mSipFormLinearLayout = (LinearLayout) inflatedView.findViewById(R.id.sipFormLinearLayout);
mSipFormLinearLayout.setVisibility(View.GONE);
inflatedView.findViewById(R.id.sipHeaderLinearLayout).setOnClickListener(new View.OnClickListener() {
@@ -146,6 +287,7 @@
public void onClick(View v) {
if (null != mSipFormLinearLayout) {
if (mSipFormLinearLayout.getVisibility() != View.VISIBLE) {
+ flipForm(false, false);
mSipFormLinearLayout.setVisibility(View.VISIBLE);
//~ Let the time to perform setVisibility before scrolling.
final ScrollView loginForm = (ScrollView) inflatedView.findViewById(R.id.login_form);
@@ -175,7 +317,7 @@
* @author paulburke
*/
@SuppressLint("NewApi")
- public static String getPath(final Context context, final Uri uri) {
+ private static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
@@ -247,8 +389,8 @@
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
- public static String getDataColumn(Context context, Uri uri, String selection,
- String[] selectionArgs) {
+ private static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
@@ -277,7 +419,7 @@
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
- public static boolean isExternalStorageDocument(Uri uri) {
+ private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
@@ -285,7 +427,7 @@
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
- public static boolean isDownloadsDocument(Uri uri) {
+ private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
@@ -293,7 +435,7 @@
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
- public static boolean isMediaDocument(Uri uri) {
+ private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
@@ -303,6 +445,67 @@
startActivityForResult(intent, FILE_SELECT_CODE);
}
+ public static long copy(Reader input, Writer output) throws IOException {
+ char[] buffer = new char[8192];
+ long count = 0;
+ int n;
+ while ((n = input.read(buffer)) != -1) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ private int getDocumentSize(Uri uri) {
+ // The query, since it only applies to a single document, will only return
+ // one row. There's no need to filter, sort, or select fields, since we want
+ // all fields for one document.
+ Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null, null);
+
+ try {
+ // moveToFirst() returns false if the cursor has 0 rows. Very handy for
+ // "if there's anything to look at, look at it" conditionals.
+ if (cursor != null && cursor.moveToFirst()) {
+
+ // Note it's called "Display Name". This is
+ // provider-specific, and might not necessarily be the file name.
+ //String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ //Log.i(TAG, "Display Name: " + displayName);
+
+ int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
+ // If the size is unknown, the value stored is null. But since an
+ // int can't be null in Java, the behavior is implementation-specific,
+ // which is just a fancy term for "unpredictable". So as
+ // a rule, check if it's null before assigning to an int. This will
+ // happen often: The storage API allows for remote files, whose
+ // size might not be locally known.
+ String size = null;
+ if (!cursor.isNull(sizeIndex)) {
+ // Technically the column stores an int, but cursor.getString()
+ // will do the conversion automatically.
+ size = cursor.getString(sizeIndex);
+ Log.i(TAG, "Size: " + size);
+ return cursor.getInt(sizeIndex);
+ } else {
+ size = "Unknown";
+ }
+ Log.i(TAG, "Size: " + size);
+
+ }
+ } finally {
+ cursor.close();
+ }
+ return 0;
+ }
+
+ private void readFromUri(Uri uri, String outPath) throws IOException {
+ if (getDocumentSize(uri) > 16 * 1024 * 1024) {
+ Toast.makeText(getActivity(), "File is too big", Toast.LENGTH_LONG).show();
+ throw new IOException("File is too big");
+ }
+ copy(new InputStreamReader(getActivity().getContentResolver().openInputStream(uri)), new FileWriter(outPath));
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -310,8 +513,16 @@
if (resultCode == Activity.RESULT_OK) {
Log.w(TAG, "onActivityResult " + data.getDataString());
this.mDataPath = getPath(getActivity(), data.getData());
- if (TextUtils.isEmpty(this.mDataPath))
- Toast.makeText(getActivity(), "Can't read " + data.getData(), Toast.LENGTH_LONG).show();
+ if (TextUtils.isEmpty(this.mDataPath)) {
+ try {
+ this.mDataPath = getContext().getCacheDir().getPath() + "/temp.gz";
+ readFromUri(data.getData(), this.mDataPath);
+ showImportDialog();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), "Can't read " + data.getData(), Toast.LENGTH_LONG).show();
+ }
+ }
else
showImportDialog();
}
@@ -434,7 +645,7 @@
* Attempts to sign in or register the account specified by the login form. If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
- public void attemptCreation() {
+ private void attemptCreation() {
// Reset errors.
mAliasView.setError(null);
@@ -474,7 +685,7 @@
} else if (warningIPAccount) {
showIP2IPDialog();
} else {
- initAccountCreation();
+ initAccountCreation(null, null, null);
}
}
@@ -485,7 +696,7 @@
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
- initAccountCreation();
+ initAccountCreation(null, null, null);
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -498,7 +709,7 @@
}
@SuppressWarnings("unchecked")
- private void initAccountCreation() {
+ private void initAccountCreation(String new_username, String pin, String password) {
try {
HashMap<String, String> accountDetails = (HashMap<String, String>) mCallbacks.getRemoteService().getAccountTemplate(mAccountType);
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_TYPE, mAccountType);
@@ -506,24 +717,30 @@
boolean hasCameraPermission = ContextCompat.checkSelfPermission(getActivity(),
Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
accountDetails.put(AccountDetailBasic.CONFIG_VIDEO_ENABLED, Boolean.toString(hasCameraPermission));
+
+ //~ Sipinfo is forced for any sipaccount since overrtp is not supported yet.
+ //~ This will have to be removed when it will be supported.
+ accountDetails.put(AccountDetailAdvanced.CONFIG_ACCOUNT_DTMF_TYPE, getString(R.string.account_sip_dtmf_type_sipinfo));
+
if (mAccountType.equals(AccountDetailBasic.ACCOUNT_TYPE_RING)) {
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_ALIAS, "Ring");
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_HOSTNAME, "bootstrap.ring.cx");
+ if (password != null && !password.isEmpty())
+ accountDetails.put(AccountDetailBasic.CONFIG_ARCHIVE_PASSWORD, password);
+ if (pin != null && !pin.isEmpty()) {
+ accountDetails.put(AccountDetailBasic.CONFIG_ARCHIVE_PIN, pin);
+ }
// Enable UPNP by default for Ring accounts
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_UPNP_ENABLE, AccountDetail.TRUE_STR);
+ createNewAccount(accountDetails, mUsername);
} else {
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_ALIAS, mAlias);
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_HOSTNAME, mHostname);
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_USERNAME, mUsername);
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_PASSWORD, mPassword);
+ createNewAccount(accountDetails, null);
}
- //~ Sipinfo is forced for any sipaccount since overrtp is not supported yet.
- //~ This will have to be removed when it will be supported.
- accountDetails.put(AccountDetailAdvanced.CONFIG_ACCOUNT_DTMF_TYPE,
- getString(R.string.account_sip_dtmf_type_sipinfo));
-
- createNewAccount(accountDetails);
} catch (RemoteException e) {
Toast.makeText(getActivity(), "Error creating account", Toast.LENGTH_SHORT).show();
e.printStackTrace();
@@ -531,8 +748,107 @@
}
- class CreateAccountTask extends AsyncTask<HashMap<String, String>, Void, String> {
+ private boolean checkPassword(@NonNull TextView pwd, TextView confirm) {
+ boolean error = false;
+ if (pwd.getText().length() == 0) {
+ error = true;
+ } else {
+ if (pwd.getText().length() < 6) {
+ pwd.setError(getString(R.string.error_password_char_count));
+ error = true;
+ } else {
+ pwd.setError(null);
+ }
+ }
+ if (confirm != null) {
+ if (!pwd.getText().toString().equals(confirm.getText().toString())) {
+ confirm.setError(getString(R.string.error_passwords_not_equals));
+ error = true;
+ } else {
+ confirm.setError(null);
+ }
+ }
+ return error;
+ }
+
+ /*private AlertDialog showPasswordDialog() {
+ Activity ownerActivity = getActivity();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(ownerActivity);
+ LayoutInflater inflater = ownerActivity.getLayoutInflater();
+ ViewGroup v = (ViewGroup) inflater.inflate(R.layout.dialog_account_export, null);
+ final TextView pwd = (TextView) v.findViewById(R.id.newpwd_txt);
+ final TextView pwd_confirm = (TextView) v.findViewById(R.id.newpwd_confirm_txt);
+ builder.setMessage(R.string.account_export_message)
+ .setTitle(R.string.account_export_title)
+ .setPositiveButton(R.string.account_export, null)
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ }
+ }).setView(v);
+
+
+ final AlertDialog alertDialog = builder.create();
+ pwd.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_NEXT)
+ return checkPassword(v, null);
+ return false;
+ }
+ });
+ pwd.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ checkPassword((TextView) v, null);
+ } else {
+ alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ }
+ }
+ });
+ pwd_confirm.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ if (!checkPassword(pwd, v)) {
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick();
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+
+
+ alertDialog.setOwnerActivity(ownerActivity);
+ alertDialog.show();
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!checkPassword(pwd, pwd_confirm)) {
+ final String pwd_txt = pwd.getText().toString();
+ alertDialog.dismiss();
+ initAccountCreation(pwd_txt);
+ }
+ }
+ });
+
+ return alertDialog;
+ }*/
+
+
+ private class CreateAccountTask extends AsyncTask<HashMap<String, String>, Void, String> {
private ProgressDialog progress = null;
+ final private String username;
+
+ CreateAccountTask(String reg_username) {
+ Log.w(TAG, "CreateAccountTask ");
+ username = reg_username;
+ }
@Override
protected void onPreExecute() {
@@ -547,33 +863,66 @@
@SafeVarargs
@Override
protected final String doInBackground(HashMap<String, String>... accs) {
- try {
- return mCallbacks.getRemoteService().addAccount(accs[0]);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String s) {
- if (progress != null) {
- progress.dismiss();
- progress = null;
- }
- Intent resultIntent = new Intent();
- getActivity().setResult(s.isEmpty() ? Activity.RESULT_CANCELED : Activity.RESULT_OK, resultIntent);
- getActivity().finish();
+ final Account acc = mCallbacks.getService().createAccount(accs[0]);
+ acc.stateListener = new Account.OnStateChangedListener() {
+ @Override
+ public void stateChanged(String state, int code) {
+ Log.w(TAG, "stateListener -> stateChanged " + state + " " + code);
+ if (!AccountDetailVolatile.STATE_INITIALIZING.contentEquals(state)) {
+ acc.stateListener = null;
+ if (progress != null) {
+ progress.dismiss();
+ progress = null;
+ }
+ //Intent resultIntent = new Intent();
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
+ dialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ //do things
+ }
+ });
+ boolean success = false;
+ switch (state) {
+ case AccountDetailVolatile.STATE_ERROR_GENERIC:
+ dialog.setTitle("Can't find account")
+ .setMessage("Account couldn't be found on the Ring network." +
+ "\nMake sure it was exported on Ring from an existing device, and that provided credentials are correct.");
+ break;
+ case AccountDetailVolatile.STATE_ERROR_NETWORK:
+ dialog.setTitle("Can't connect to the network")
+ .setMessage("Could not add account because Ring coudn't connect to the distributed network. Check your device connectivity.");
+ break;
+ default:
+ dialog.setTitle("Account device added")
+ .setMessage("You have successfully setup your Ring account on this device.");
+ success = true;
+ break;
+ }
+ AlertDialog d = dialog.show();
+ if (success) {
+ d.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ getActivity().setResult(Activity.RESULT_OK, new Intent());
+ getActivity().finish();
+ }
+ });
+ }
+ }
+ }
+ };
+ Log.w(TAG, "Account created, registering " + username);
+ return acc.getAccountID();
}
}
- private void createNewAccount(HashMap<String, String> accountDetails) {
+ private void createNewAccount(HashMap<String, String> accountDetails, String register_name) {
if (creatingAccount)
return;
creatingAccount = true;
//noinspection unchecked
- new CreateAccountTask().execute(accountDetails);
+ new CreateAccountTask(register_name).execute(accountDetails);
}
-}
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
index 37aae24..4fd1a0e 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
@@ -240,9 +240,9 @@
entryView.alias.setText(item.getAlias());
if (item.isIP2IP())
- entryView.host.setText(item.getRegistered_state());
+ entryView.host.setText(item.getRegistrationState());
else if (item.isSip())
- entryView.host.setText(item.getHost() + " - " + item.getRegistered_state());
+ entryView.host.setText(item.getHost() + " - " + item.getRegistrationState());
else
entryView.host.setText(item.getBasicDetails().getDetailString(AccountDetailBasic.CONFIG_ACCOUNT_USERNAME));
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/Account.java b/ring-android/app/src/main/java/cx/ring/model/account/Account.java
index a65282d..fba2ee5 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/Account.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/Account.java
@@ -32,13 +32,25 @@
public class Account extends java.util.Observable {
private static final String TAG = "Account";
- final String accountID;
+ private final String accountID;
private AccountDetailBasic basicDetails = null;
private AccountDetailAdvanced advancedDetails = null;
private AccountDetailSrtp srtpDetails = null;
private AccountDetailTls tlsDetails = null;
private AccountDetailVolatile volatileDetails = null;
- private ArrayList<AccountCredentials> credentialsDetails;
+ private ArrayList<AccountCredentials> credentialsDetails = new ArrayList<>();
+
+ private Map<String, String> devices = new HashMap<>();
+
+ public OnDevicesChangedListener devicesListener = null;
+ public OnExportEndedListener exportListener = null;
+ public OnStateChangedListener stateListener = null;
+
+ public Account(String bAccountID) {
+ accountID = bAccountID;
+ volatileDetails = new AccountDetailVolatile();
+ basicDetails = new AccountDetailBasic();
+ }
public Account(String bAccountID, final Map<String, String> details, final ArrayList<Map<String, String>> credentials, final Map<String, String> volatile_details) {
accountID = bAccountID;
@@ -48,14 +60,56 @@
tlsDetails = new AccountDetailTls(details);
if (volatile_details != null)
volatileDetails = new AccountDetailVolatile(volatile_details);
+ setCredentials(credentials);
+ }
+
+ public void update(Account acc) {
+ String old = getRegistrationState();
+ basicDetails = acc.basicDetails;
+ advancedDetails = acc.advancedDetails;
+ srtpDetails = acc.srtpDetails;
+ tlsDetails = acc.tlsDetails;
+ volatileDetails = acc.volatileDetails;
+ credentialsDetails = acc.credentialsDetails;
+ devices = acc.devices;
+ String new_reg_state = getRegistrationState();
+ if (!old.contentEquals(new_reg_state)) {
+ if (stateListener != null) {
+ stateListener.stateChanged(new_reg_state, getRegistrationStateCode());
+ }
+ }
+ }
+
+ public Map<String, String> getDevices() {
+ return devices;
+ }
+
+ public void setCredentials(ArrayList<Map<String, String>> credentials) {
+ credentialsDetails.clear();
if (credentials != null) {
- credentialsDetails = new ArrayList<>();
+ credentialsDetails.ensureCapacity(credentials.size());
for (int i = 0; i < credentials.size(); ++i) {
credentialsDetails.add(new AccountCredentials(credentials.get(i)));
}
}
}
+ public interface OnDevicesChangedListener {
+ void devicesChanged(Map<String, String> devices);
+ }
+ public interface OnExportEndedListener {
+ void exportEnded(int code, String pin);
+ }
+ public interface OnStateChangedListener {
+ void stateChanged(String state, int code);
+ }
+
+ public void setDevices(Map<String, String> devs) {
+ devices = devs;
+ if (devicesListener != null)
+ devicesListener.devicesChanged(devs);
+ }
+
public String getAccountID() {
return accountID;
}
@@ -76,18 +130,37 @@
basicDetails.setDetailString(AccountDetailBasic.CONFIG_ACCOUNT_ROUTESET, proxy);
}
- public String getRegistered_state() {
+ public String getRegistrationState() {
return volatileDetails.getDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATUS);
}
+ public int getRegistrationStateCode() {
+ String code_str = volatileDetails.getDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE);
+ if (code_str == null || code_str.isEmpty())
+ return 0;
+ return Integer.parseInt(code_str);
+ }
public void setRegistrationState(String registered_state, int code) {
Log.i(TAG, "setRegistrationState " + registered_state + " " + code);
+ String old = getRegistrationState();
volatileDetails.setDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATUS, registered_state);
volatileDetails.setDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, Integer.toString(code));
+ if (!old.contentEquals(registered_state)) {
+ if (stateListener != null) {
+ stateListener.stateChanged(registered_state, code);
+ }
+ }
}
public void setVolatileDetails(Map<String, String> volatile_details) {
+ String state_old = getRegistrationState();
volatileDetails = new AccountDetailVolatile(volatile_details);
+ String state_new = getRegistrationState();
+ if (!state_old.contentEquals(state_new)) {
+ if (stateListener != null) {
+ stateListener.stateChanged(state_new, getRegistrationStateCode());
+ }
+ }
}
public String getAlias() {
@@ -110,8 +183,8 @@
return basicDetails;
}
- public void setBasicDetails(AccountDetailBasic basicDetails) {
- this.basicDetails = basicDetails;
+ public void setBasicDetails(final Map<String, String> details) {
+ this.basicDetails = new AccountDetailBasic(details);
}
public AccountDetailAdvanced getAdvancedDetails() {
@@ -158,15 +231,15 @@
}
public boolean isTrying() {
- return getRegistered_state().contentEquals(AccountDetailVolatile.STATE_TRYING);
+ return getRegistrationState().contentEquals(AccountDetailVolatile.STATE_TRYING);
}
public boolean isRegistered() {
- return (getRegistered_state().contentEquals(AccountDetailVolatile.STATE_READY) || getRegistered_state().contentEquals(AccountDetailVolatile.STATE_REGISTERED));
+ return (getRegistrationState().contentEquals(AccountDetailVolatile.STATE_READY) || getRegistrationState().contentEquals(AccountDetailVolatile.STATE_REGISTERED));
}
public boolean isInError() {
- String state = getRegistered_state();
+ String state = getRegistrationState();
return (state.contentEquals(AccountDetailVolatile.STATE_ERROR)
|| state.contentEquals(AccountDetailVolatile.STATE_ERROR_AUTH)
|| state.contentEquals(AccountDetailVolatile.STATE_ERROR_CONF_STUN)
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
index e92494d..4870920 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
@@ -79,7 +79,7 @@
CONFIG_PUBLISHED_SAMEAS_LOCAL,
CONFIG_STUN_ENABLE, CONFIG_TURN_ENABLE));
- private ArrayList<AccountDetail.PreferenceEntry> privateArray;
+ private final ArrayList<AccountDetail.PreferenceEntry> privateArray;
public AccountDetailAdvanced(Map<String, String> pref) {
privateArray = new ArrayList<>(pref.size());
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
index 1a78022..3047552 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
@@ -51,6 +51,10 @@
public static final String CONFIG_PRESENCE_ENABLE = "Account.presenceEnabled";
+ public static final String CONFIG_ARCHIVE_PASSWORD = "Account.archivePassword";
+ public static final String CONFIG_ARCHIVE_PIN = "Account.archivePIN";
+ public static final String CONFIG_ETH_ACCOUNT = "ETH.account";
+
public static final String ACCOUNT_TYPE_RING = "RING";
public static final String ACCOUNT_TYPE_SIP = "SIP";
@@ -60,7 +64,7 @@
CONFIG_ACCOUNT_UPNP_ENABLE,
CONFIG_VIDEO_ENABLED));
- private ArrayList<AccountDetail.PreferenceEntry> privateArray;
+ private final ArrayList<AccountDetail.PreferenceEntry> privateArray;
public String getAlias() {
return getDetailString(CONFIG_ACCOUNT_ALIAS);
@@ -74,6 +78,10 @@
return getDetailString(CONFIG_ACCOUNT_HOSTNAME);
}
+ public AccountDetailBasic() {
+ privateArray = new ArrayList<>();
+ }
+
public AccountDetailBasic(Map<String, String> pref) {
privateArray = new ArrayList<>(pref.size());
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
index 4d4bf95..b5937b7 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
@@ -43,11 +43,10 @@
public static final String CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE = "TLS.requireClientCertificate";
public static final String CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC = "TLS.negotiationTimeoutSec";
- private ArrayList<AccountDetail.PreferenceEntry> privateArray;
+ private final ArrayList<AccountDetail.PreferenceEntry> privateArray = getPreferenceEntries();
public static ArrayList<AccountDetail.PreferenceEntry> getPreferenceEntries() {
ArrayList<AccountDetail.PreferenceEntry> preference = new ArrayList<>();
-
preference.add(new PreferenceEntry(CONFIG_TLS_LISTENER_PORT));
preference.add(new PreferenceEntry(CONFIG_TLS_ENABLE, true));
preference.add(new PreferenceEntry(CONFIG_TLS_CA_LIST_FILE));
@@ -61,13 +60,10 @@
preference.add(new PreferenceEntry(CONFIG_TLS_VERIFY_CLIENT, true));
preference.add(new PreferenceEntry(CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, true));
preference.add(new PreferenceEntry(CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC));
-
return preference;
}
public AccountDetailTls(Map<String, String> pref) {
- privateArray = getPreferenceEntries();
-
for (AccountDetail.PreferenceEntry p : privateArray) {
p.mValue = pref.get(p.mKey);
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailVolatile.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailVolatile.java
index 3f39d9e..26718e4 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailVolatile.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailVolatile.java
@@ -49,12 +49,13 @@
public static final String STATE_ERROR_SERVICE_UNAVAILABLE = "ERROR_SERVICE_UNAVAILABLE";
public static final String STATE_ERROR_NOT_ACCEPTABLE = "ERROR_NOT_ACCEPTABLE";
public static final String STATE_REQUEST_TIMEOUT = "Request Timeout";
+ public static final String STATE_INITIALIZING = "INITIALIZING";
- private ArrayList<PreferenceEntry> privateArray;
+ private final ArrayList<PreferenceEntry> privateArray = new ArrayList<>();
+ public AccountDetailVolatile() {
+ }
public AccountDetailVolatile(Map<String, String> pref) {
- privateArray = new ArrayList<PreferenceEntry>();
-
for (String key : pref.keySet()) {
PreferenceEntry p = new PreferenceEntry(key);
p.mValue = pref.get(key);
diff --git a/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java b/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
index 7562d04..39bac20 100644
--- a/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
+++ b/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
@@ -28,11 +28,13 @@
import cx.ring.BuildConfig;
-public class ConfigurationManagerCallback extends ConfigurationCallback {
+class ConfigurationManagerCallback extends ConfigurationCallback {
private static final String TAG = ConfigurationManagerCallback.class.getSimpleName();
static public final String ACCOUNTS_CHANGED = BuildConfig.APPLICATION_ID + "accounts.changed";
+ static public final String ACCOUNTS_DEVICES_CHANGED = BuildConfig.APPLICATION_ID + "accounts.devicesChanged";
+ static public final String ACCOUNTS_EXPORT_ENDED = BuildConfig.APPLICATION_ID + "accounts.exportEnded";
static public final String ACCOUNT_STATE_CHANGED = BuildConfig.APPLICATION_ID + "account.stateChanged";
static public final String INCOMING_TEXT = BuildConfig.APPLICATION_ID + ".message.incomingTxt";
static public final String MESSAGE_STATE_CHANGED = BuildConfig.APPLICATION_ID + ".message.stateChanged";
@@ -116,17 +118,39 @@
OpenSlParams audioParams = OpenSlParams.createInstance(mService);
ret.add(audioParams.getSampleRate());
ret.add(audioParams.getBufferSize());
- Log.d(getClass().getName(), "getHardwareAudioFormat: " + audioParams.getSampleRate() + " " + audioParams.getBufferSize());
+ Log.d(TAG, "getHardwareAudioFormat: " + audioParams.getSampleRate() + " " + audioParams.getBufferSize());
}
@Override
public void getAppDataPath(String name, StringVect ret) {
- if (name.equals("files"))
- ret.add(mService.getFilesDir().getAbsolutePath());
- else if (name.equals("cache"))
- ret.add(mService.getCacheDir().getAbsolutePath());
- else
- ret.add(mService.getDir(name, Context.MODE_PRIVATE).getAbsolutePath());
+ switch (name) {
+ case "files":
+ ret.add(mService.getFilesDir().getAbsolutePath());
+ break;
+ case "cache":
+ ret.add(mService.getCacheDir().getAbsolutePath());
+ break;
+ default:
+ ret.add(mService.getDir(name, Context.MODE_PRIVATE).getAbsolutePath());
+ break;
+ }
}
+ @Override
+ public void knownDevicesChanged(String account, StringMap devices) {
+ Intent intent = new Intent(ACCOUNTS_DEVICES_CHANGED);
+ intent.putExtra("account", account);
+ intent.putExtra("devices", devices.toNative());
+ mService.sendBroadcast(intent);
+ }
+
+ @Override
+ public void exportOnRingEnded(String account, int code, String pin) {
+ Log.w(TAG, "exportOnRingEnded: " + account + " " + code + " " + pin);
+ Intent intent = new Intent(ACCOUNTS_EXPORT_ENDED);
+ intent.putExtra("account", account);
+ intent.putExtra("code", code);
+ intent.putExtra("pin", pin);
+ mService.sendBroadcast(intent);
+ }
}
diff --git a/ring-android/app/src/main/java/cx/ring/service/DRingService.java b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
index 30b288d..b74fdd4 100644
--- a/ring-android/app/src/main/java/cx/ring/service/DRingService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
@@ -852,6 +852,27 @@
});
}
+ @Override
+ public String exportOnRing(final String accountId, final String password) {
+ return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
+ @Override
+ protected String doRun() throws SameThreadException {
+ Log.i(TAG, "DRingService.addRingDevice() thread running...");
+ return Ringservice.exportOnRing(accountId, password);
+ }
+ });
+ }
+
+ public Map<String, String> getKnownRingDevices(final String accountId) {
+ return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
+ @Override
+ protected Map<String, String> doRun() throws SameThreadException {
+ Log.i(TAG, "DRingService.getKnownRingDevices() thread running...");
+ return Ringservice.getKnownRingDevices(accountId).toNative();
+ }
+ });
+ }
+
/*************************
* Transfer related API
*************************/
diff --git a/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl b/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
index a4ffc7e..020ea0b 100644
--- a/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
+++ b/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
@@ -49,6 +49,8 @@
String getCurrentAudioOutputPlugin();
List getCodecList(in String accountID);
void setActiveCodecList(in List codecs, in String accountID);
+ String exportOnRing(in String accountID, in String password);
+ Map getKnownRingDevices(in String accountID);
Map validateCertificatePath(in String accountID, in String certificatePath, in String privateKeyPath, in String privateKeyPass);
Map validateCertificate(in String accountID, in String certificateId);
diff --git a/ring-android/app/src/main/java/cx/ring/service/LocalService.java b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
index 5b459cc..04990a2 100644
--- a/ring-android/app/src/main/java/cx/ring/service/LocalService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
@@ -91,9 +91,11 @@
import cx.ring.model.SipUri;
import cx.ring.model.TextMessage;
import cx.ring.model.account.Account;
-import cx.ring.model.account.AccountDetailAdvanced;
+import cx.ring.model.account.AccountCredentials;
+import cx.ring.model.account.AccountDetailBasic;
import cx.ring.model.account.AccountDetailSrtp;
import cx.ring.model.account.AccountDetailTls;
+import cx.ring.model.account.AccountDetailVolatile;
import cx.ring.utils.MediaManager;
public class LocalService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
@@ -216,10 +218,26 @@
return conf;
}
+ public Account createAccount(HashMap<String, String> conf) {
+ Account acc = null;
+ try {
+ final String acc_id = mService.addAccount(conf);
+ acc = getAccount(acc_id);
+ if (acc == null) {
+ acc = new Account(acc_id);
+ accounts.add(acc);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return acc;
+ }
+
public void sendTextMessage(String account, SipUri to, String txt) {
try {
long id = mService.sendAccountTextMessage(account, to.getRawUriString(), txt);
Log.w(TAG, "sendAccountTextMessage " + txt + " got id " + id);
+ Log.w(TAG, "sendAccountTextMessage " + txt + " got id " + id);
TextMessage message = new TextMessage(false, txt, to, null, account);
message.setID(id);
message.read();
@@ -378,29 +396,22 @@
@Override
public void onLoadComplete(Loader<ArrayList<Account>> loader, ArrayList<Account> data) {
Log.w(TAG, "AccountsLoader Loader.OnLoadCompleteListener " + data.size());
- accounts = data;
+ ArrayList<Account> naccs = new ArrayList<>(data.size());
+ for (Account acc : data) {
+ Account f = getAccount(acc.getAccountID());
+ if (f != null)
+ f.update(acc);
+ else
+ f = acc;
+ naccs.add(f);
+ }
+ accounts = naccs;
mAccountLoader.stopLoading();
boolean haveSipAccount = false;
boolean haveRingAccount = false;
for (Account acc : accounts) {
- //~ Sipinfo is forced for any sipaccount since overrtp is not supported yet.
- //~ This will have to be removed when it will be supported.
- Log.d(TAG, "Settings SIP DTMF type to sipinfo");
- acc.getAdvancedDetails().setDetailString(
- AccountDetailAdvanced.CONFIG_ACCOUNT_DTMF_TYPE,
- getString(R.string.account_sip_dtmf_type_sipinfo)
- );
-
- try {
- final IDRingService remote = getRemoteService();
- remote.setAccountDetails(acc.getAccountID(), acc.getDetails());
- } catch (android.os.RemoteException exception) {
- Log.e(TAG, "AccountsLoader", exception);
- }
-
- if (!acc.isEnabled()) {
+ if (!acc.isEnabled())
continue;
- }
if (acc.isSip()) {
haveSipAccount = true;
} else if (acc.isRing()) {
@@ -1295,13 +1306,25 @@
mAccountLoader.startLoading();
mAccountLoader.onContentChanged();
} else {
- for (Account a : accounts) {
- if (a.getAccountID().contentEquals(intent.getStringExtra("account"))) {
+ Account a = getAccount(intent.getStringExtra("account"));
+ if (a != null) {
+ String state_old = a.getRegistrationState();
+ String state_new = intent.getStringExtra("state");
+ if (state_old.contentEquals(AccountDetailVolatile.STATE_INITIALIZING) &&
+ !state_new.contentEquals(AccountDetailVolatile.STATE_INITIALIZING)) {
+ try {
+ a.setBasicDetails((Map<String, String>) mService.getAccountDetails(a.getAccountID()));
+ a.setCredentials((ArrayList<Map<String, String>>) mService.getCredentials(a.getAccountID()));
+ a.setDevices((Map<String, String>) mService.getKnownRingDevices(a.getAccountID()));
+ a.setVolatileDetails((Map<String, String>) mService.getVolatileAccountDetails(a.getAccountID()));
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ } else {
a.setRegistrationState(intent.getStringExtra("state"), intent.getIntExtra("code", 0));
- sendBroadcast(new Intent(ACTION_ACCOUNT_UPDATE));
- break;
}
}
+ sendBroadcast(new Intent(ACTION_ACCOUNT_UPDATE));
}
break;
case ConfigurationManagerCallback.ACCOUNTS_CHANGED:
@@ -1310,6 +1333,18 @@
mAccountLoader.onContentChanged();
}
break;
+ case ConfigurationManagerCallback.ACCOUNTS_DEVICES_CHANGED: {
+ Account acc = getAccount(intent.getStringExtra("account"));
+ acc.setDevices((Map<String, String>) intent.getSerializableExtra("devices"));
+ break;
+ }
+ case ConfigurationManagerCallback.ACCOUNTS_EXPORT_ENDED: {
+ Account acc = getAccount(intent.getStringExtra("account"));
+ if (acc != null && acc.exportListener != null) {
+ acc.exportListener.exportEnded(intent.getIntExtra("code", -1), intent.getStringExtra("pin"));
+ }
+ break;
+ }
case CallManagerCallBack.INCOMING_TEXT:
case ConfigurationManagerCallback.INCOMING_TEXT: {
String message = intent.getStringExtra("txt");
@@ -1470,6 +1505,8 @@
intentFilter.addAction(ConfigurationManagerCallback.ACCOUNT_STATE_CHANGED);
intentFilter.addAction(ConfigurationManagerCallback.ACCOUNTS_CHANGED);
+ intentFilter.addAction(ConfigurationManagerCallback.ACCOUNTS_EXPORT_ENDED);
+ intentFilter.addAction(ConfigurationManagerCallback.ACCOUNTS_DEVICES_CHANGED);
intentFilter.addAction(ConfigurationManagerCallback.INCOMING_TEXT);
intentFilter.addAction(ConfigurationManagerCallback.MESSAGE_STATE_CHANGED);
diff --git a/ring-android/app/src/main/java/cx/ring/views/BoundedLinearLayout.java b/ring-android/app/src/main/java/cx/ring/views/BoundedLinearLayout.java
new file mode 100644
index 0000000..f19dbee
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/views/BoundedLinearLayout.java
@@ -0,0 +1,46 @@
+package cx.ring.views;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import cx.ring.R;
+
+public class BoundedLinearLayout extends LinearLayout {
+
+ private final int mBoundedWidth;
+
+ private final int mBoundedHeight;
+
+ public BoundedLinearLayout(Context context) {
+ super(context);
+ mBoundedWidth = 0;
+ mBoundedHeight = 0;
+ }
+
+ public BoundedLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BoundedView);
+ mBoundedWidth = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_width, 0);
+ mBoundedHeight = a.getDimensionPixelSize(R.styleable.BoundedView_bounded_height, 0);
+ a.recycle();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Adjust width as necessary
+ int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
+ if(mBoundedWidth > 0 && mBoundedWidth < measuredWidth) {
+ int measureMode = MeasureSpec.getMode(widthMeasureSpec);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedWidth, measureMode);
+ }
+ // Adjust height as necessary
+ int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
+ if(mBoundedHeight > 0 && mBoundedHeight < measuredHeight) {
+ int measureMode = MeasureSpec.getMode(heightMeasureSpec);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mBoundedHeight, measureMode);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/jni/configurationmanager.i b/ring-android/app/src/main/jni/configurationmanager.i
index a8d92a8..384a039 100644
--- a/ring-android/app/src/main/jni/configurationmanager.i
+++ b/ring-android/app/src/main/jni/configurationmanager.i
@@ -34,6 +34,8 @@
virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map<std::string, std::string>& details){}
virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map<std::string, std::string>& /*payload*/){}
virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+ virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map<std::string, std::string>& /*devices*/){}
+ virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
virtual void incomingTrustRequest(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector<uint8_t>& /*payload*/, time_t received){}
@@ -73,6 +75,8 @@
std::map<std::string, std::string> getCodecDetails(const std::string& accountID, const unsigned& codecId);
bool setCodecDetails(const std::string& accountID, const unsigned& codecId, const std::map<std::string, std::string>& details);
std::vector<unsigned> getActiveCodecList(const std::string& accountID);
+std::string exportOnRing(const std::string& accountID, const std::string& password);
+std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID);
void setActiveCodecList(const std::string& accountID, const std::vector<unsigned>& list);
@@ -183,6 +187,8 @@
virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map<std::string, std::string>& details){}
virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map<std::string, std::string>& /*payload*/){}
virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+ virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map<std::string, std::string>& /*devices*/){}
+ virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
virtual void incomingTrustRequest(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector<uint8_t>& /*payload*/, time_t received){}
diff --git a/ring-android/app/src/main/jni/jni_interface.i b/ring-android/app/src/main/jni/jni_interface.i
index 4a5dab5..bdf7767 100644
--- a/ring-android/app/src/main/jni/jni_interface.i
+++ b/ring-android/app/src/main/jni/jni_interface.i
@@ -218,6 +218,8 @@
exportable_callback<ConfigurationSignal::StunStatusFailed>(bind(&ConfigurationCallback::stunStatusFailure, confM, _1)),
exportable_callback<ConfigurationSignal::RegistrationStateChanged>(bind(&ConfigurationCallback::registrationStateChanged, confM, _1, _2, _3, _4)),
exportable_callback<ConfigurationSignal::VolatileDetailsChanged>(bind(&ConfigurationCallback::volatileAccountDetailsChanged, confM, _1, _2)),
+ exportable_callback<ConfigurationSignal::KnownDevicesChanged>(bind(&ConfigurationCallback::knownDevicesChanged, confM, _1, _2)),
+ exportable_callback<ConfigurationSignal::ExportOnRingEnded>(bind(&ConfigurationCallback::exportOnRingEnded, confM, _1, _2, _3)),
exportable_callback<ConfigurationSignal::Error>(bind(&ConfigurationCallback::errorAlert, confM, _1)),
exportable_callback<ConfigurationSignal::IncomingAccountMessage>(bind(&ConfigurationCallback::incomingAccountMessage, confM, _1, _2, _3 )),
exportable_callback<ConfigurationSignal::AccountMessageStatusChanged>(bind(&ConfigurationCallback::accountMessageStatusChanged, confM, _1, _2, _3, _4 )),
diff --git a/ring-android/app/src/main/res/layout/frag_account_creation.xml b/ring-android/app/src/main/res/layout/frag_account_creation.xml
index a6249f7..82ef5cd 100644
--- a/ring-android/app/src/main/res/layout/frag_account_creation.xml
+++ b/ring-android/app/src/main/res/layout/frag_account_creation.xml
@@ -19,33 +19,35 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/login_form"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:background="#eeeeee"
tools:context=".client.AccountWizard">
- <LinearLayout
+ <cx.ring.views.BoundedLinearLayout
style="@style/AccountFormContainer"
+ android:layout_gravity="center_horizontal"
+ android:animateLayoutChanges="false"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
- android:orientation="vertical">
+ android:orientation="vertical"
+ app:bounded_width="320dp">
- <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ <android.support.v7.widget.CardView
android:id="@+id/ring_card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginBottom="8dp"
- android:clickable="true"
- android:foreground="?android:attr/selectableItemBackground"
- card_view:cardCornerRadius="2dp">
+ android:layout_marginBottom="16dp"
+ android:animateLayoutChanges="false">
<RelativeLayout
android:id="@+id/ring_fields"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:animateLayoutChanges="true"
android:orientation="vertical">
<ImageView
@@ -71,8 +73,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="24dp"
- android:singleLine="false"
- android:text="@string/help_ring_title"
+ android:text="Ring account"
android:textColor="@color/text_color_primary"
android:textSize="24sp" />
@@ -87,72 +88,103 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
- android:singleLine="false"
android:text="@string/help_ring"
android:textColor="@color/text_color_primary"
android:textSize="14sp" />
- </RelativeLayout>
+ <LinearLayout
+ android:id="@+id/addAccountLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/textView"
+ android:orientation="vertical"
+ android:visibility="gone">
- </android.support.v7.widget.CardView>
+ <EditText
+ android:id="@+id/ring_add_pin"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:layout_marginStart="12dp"
+ android:hint="Enter PIN"
+ android:maxLines="1"
+ android:inputType="text"
+ android:imeOptions="actionNext" />
- <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
- android:id="@+id/import_card_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginBottom="8dp"
- android:clickable="true"
- android:foreground="?android:attr/selectableItemBackground"
- card_view:cardCornerRadius="2dp">
+ <EditText
+ android:id="@+id/ring_add_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:layout_marginStart="12dp"
+ android:hint="Enter your password"
+ android:inputType="textPassword" />
+ </LinearLayout>
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:nestedScrollingEnabled="false"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/import_acc_title_txt"
+ <Button
+ android:id="@+id/ring_add_account"
+ style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="false"
- android:layout_alignParentTop="true"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingTop="24dp"
- android:singleLine="false"
- android:text="@string/account_import_title"
- android:textColor="@color/text_color_primary"
- android:textSize="24sp" />
+ android:layout_below="@id/addAccountLayout"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="Add existing account" />
- <ImageView
- android:id="@+id/imageView4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_margin="16dp"
- android:contentDescription="@string/account_import_title"
- android:src="@drawable/ic_archive_black_48dp" />
-
- <TextView
- android:id="@+id/importAccountExplanation"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/import_acc_title_txt"
- android:paddingBottom="24dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingTop="16dp"
- android:singleLine="false"
- android:text="@string/account_import_explanation"
- android:textColor="@color/text_color_primary"
- android:textSize="14sp" />
+ android:layout_height="match_parent"
+ android:layout_below="@id/ring_add_account"
+ android:background="@color/color_primary_light"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:id="@+id/newAccountLayout"
+ android:background="@color/color_primary_light"
+ android:visibility="gone">
+
+ <EditText
+ android:id="@+id/ring_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:hint="@string/prompt_new_password"
+ android:inputType="textPassword"
+ android:imeOptions="actionNext" />
+
+ <EditText
+ android:id="@+id/ring_password_repeat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:hint="@string/prompt_new_password_repeat"
+ android:imeActionId="@integer/register_sip_account_actionid"
+ android:imeActionLabel="@string/action_create_short"
+ android:inputType="textPassword" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/ring_create_btn"
+ style="?attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="Create new account"
+ android:textColor="@color/text_color_primary_dark" />
+ </LinearLayout>
</RelativeLayout>
@@ -164,14 +196,15 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
+ android:animateLayoutChanges="false"
android:clickable="true"
- android:foreground="?android:attr/selectableItemBackground"
- card_view:cardCornerRadius="2dp">
+ android:foreground="?android:attr/selectableItemBackground">
<RelativeLayout
android:id="@+id/sip_fields"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:animateLayoutChanges="true"
android:nestedScrollingEnabled="false"
android:orientation="vertical">
@@ -296,6 +329,6 @@
</android.support.v7.widget.CardView>
- </LinearLayout>
+ </cx.ring.views.BoundedLinearLayout>
</ScrollView>
diff --git a/ring-android/app/src/main/res/layout/frag_accounts_list.xml b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
index 6cd6910..e04047f 100644
--- a/ring-android/app/src/main/res/layout/frag_accounts_list.xml
+++ b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
@@ -1,4 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2016 Savoir-faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
diff --git a/ring-android/app/src/main/res/layout/item_account.xml b/ring-android/app/src/main/res/layout/item_account.xml
index 41a03af..33cacc5 100644
--- a/ring-android/app/src/main/res/layout/item_account.xml
+++ b/ring-android/app/src/main/res/layout/item_account.xml
@@ -1,4 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2016 Savoir-faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/ring-android/app/src/main/res/values/attrs.xml b/ring-android/app/src/main/res/values/attrs.xml
index db3e679..fe95528 100644
--- a/ring-android/app/src/main/res/values/attrs.xml
+++ b/ring-android/app/src/main/res/values/attrs.xml
@@ -72,5 +72,9 @@
<attr name="use_default_controller" format="boolean" />
</declare-styleable>
+ <declare-styleable name="BoundedView">
+ <attr name="bounded_width" format="dimension" />
+ <attr name="bounded_height" format="dimension" />
+ </declare-styleable>
</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index eb67a96..e762524 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -164,5 +164,7 @@
<!-- Contacts -->
<string name="add_call_contact_number_to_contacts">Add %1$s ?</string>
+ <string name="prompt_new_password">New password</string>
+ <string name="prompt_new_password_repeat">Repeat new password</string>
</resources>