multi-device: account migration
- detects a need for migration in Home screen
- offers a migration dialog in Account management screen
Tuleap: #955
Change-Id: Ia7bce09720fd0ad08a624aafde69e1be304c42ef
diff --git a/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java b/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
index cffd9d3..d4a32ef 100644
--- a/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
+++ b/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
@@ -21,18 +21,19 @@
package cx.ring.client;
+import android.app.Fragment;
+import android.app.FragmentManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
@@ -41,11 +42,12 @@
import cx.ring.R;
import cx.ring.fragments.AccountCreationFragment;
+import cx.ring.fragments.AccountMigrationFragment;
import cx.ring.service.IDRingService;
import cx.ring.service.LocalService;
public class AccountWizard extends AppCompatActivity implements LocalService.Callbacks {
- static final String TAG = "AccountWizard";
+ static final String TAG = AccountWizard.class.getName();
private boolean mBound = false;
private LocalService service;
ViewPager mViewPager;
@@ -60,7 +62,7 @@
@Override
public void onServiceDisconnected(ComponentName arg0) {
-
+ // nothing to be done here
}
};
@@ -75,8 +77,15 @@
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
- SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(AccountWizard.this, getSupportFragmentManager());
- mViewPager.setAdapter(mSectionsPagerAdapter);
+
+ if (getIntent().getData() != null && !TextUtils.isEmpty(getIntent().getData().getLastPathSegment())) {
+ String accountId = getIntent().getData().getLastPathSegment();
+ SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(AccountWizard.this, getFragmentManager(), accountId);
+ mViewPager.setAdapter(mSectionsPagerAdapter);
+ } else {
+ SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(AccountWizard.this, getFragmentManager());
+ mViewPager.setAdapter(mSectionsPagerAdapter);
+ }
if (!mBound) {
Log.i(TAG, "onCreate: Binding service...");
@@ -99,25 +108,39 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
}
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
-
- Context mContext;
- ArrayList<Fragment> fragments;
+ private final Context mContext;
+ private final ArrayList<Fragment> fragments;
+ private final String mAccountId;
public SectionsPagerAdapter(Context c, FragmentManager fm) {
+ this(c, fm, null);
+ }
+
+ public SectionsPagerAdapter(Context c, FragmentManager fm, String accountId) {
super(fm);
mContext = c;
fragments = new ArrayList<>();
- fragments.add(new AccountCreationFragment());
+ mAccountId = accountId;
+ if (TextUtils.isEmpty(mAccountId)) {
+ fragments.add(new AccountCreationFragment());
+ } else {
+ AccountMigrationFragment fragment = new AccountMigrationFragment();
+ // give the installation id to display
+ Bundle bundle = new Bundle();
+ bundle.putString(AccountMigrationFragment.ACCOUNT_ID, mAccountId);
+ fragment.setArguments(bundle);
+ fragments.add(fragment);
+ }
}
@Override
@@ -129,16 +152,19 @@
String name;
switch (i) {
- case 0:
- name = AccountCreationFragment.class.getName();
- break;
+ case 0:
+ if (TextUtils.isEmpty(mAccountId)) {
+ name = AccountCreationFragment.class.getName();
+ } else {
+ name = AccountMigrationFragment.class.getName();
+ }
+ break;
- default:
- Log.e(TAG, "getClassName: unknown fragment position " + i);
- return null;
+ default:
+ Log.e(TAG, "getClassName: unknown fragment position " + i);
+ return null;
}
- // Log.w(TAG, "getClassName: name=" + name);
return name;
}
@@ -150,11 +176,11 @@
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
- case 0:
- return mContext.getString(R.string.title_section0).toUpperCase(Locale.getDefault());
- default:
- Log.e(TAG, "getPageTitle: unknown tab position " + position);
- break;
+ case 0:
+ return mContext.getString(R.string.title_section0).toUpperCase(Locale.getDefault());
+ default:
+ Log.e(TAG, "getPageTitle: unknown tab position " + position);
+ break;
}
return null;
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
index 94d98df..eab9edc 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
@@ -49,7 +49,6 @@
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
-import android.text.Layout;
import android.transition.Explode;
import android.transition.Fade;
import android.util.Log;
@@ -101,6 +100,7 @@
private LocalService service;
private boolean mBound = false;
private boolean mNoAccountOpened = false;
+ private boolean mIsMigrationDialogAlreadyShowed;
private NavigationView fMenu;
private MenuHeaderView fMenuHead = null;
@@ -149,8 +149,8 @@
setSupportActionBar(mToolbar);
actionButton = (FloatingActionButton) findViewById(R.id.action_button);
- mToolbarSpacerView = (LinearLayout)findViewById(R.id.toolbar_spacer);
- mToolbarSpacerTitle = (TextView)findViewById(R.id.toolbar_spacer_title);
+ mToolbarSpacerView = (LinearLayout) findViewById(R.id.toolbar_spacer);
+ mToolbarSpacerTitle = (TextView) findViewById(R.id.toolbar_spacer_title);
fMenu = (NavigationView) findViewById(R.id.left_drawer);
fMenu.setNavigationItemSelectedListener(this);
@@ -186,7 +186,7 @@
if (toRequest.length > 0) {
ActivityCompat.requestPermissions(this, toRequest, LocalService.PERMISSIONS_REQUEST);
} else if (!mBound) {
- Log.i(TAG, "onCreate: Binding service...");
+ Log.d(TAG, "onCreate: Binding service...");
Intent intent = new Intent(this, LocalService.class);
startService(intent);
bindService(intent, mConnection, BIND_AUTO_CREATE | BIND_IMPORTANT | BIND_ABOVE_CLIENT);
@@ -217,9 +217,17 @@
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Log.w(TAG, "onReceive " + intent.getAction());
+ Log.d(TAG, "onReceive " + intent.getAction());
switch (intent.getAction()) {
case LocalService.ACTION_ACCOUNT_UPDATE:
+
+ for (Account account : service.getAccounts()) {
+
+ if (account.needsMigration()) {
+ showMigrationDialog();
+ }
+ }
+
if (!mNoAccountOpened && service.getAccounts().isEmpty()) {
mNoAccountOpened = true;
startActivityForResult(new Intent().setClass(HomeActivity.this, AccountWizard.class), AccountsManagementFragment.ACCOUNT_CREATE_REQUEST);
@@ -231,6 +239,50 @@
}
};
+ private void showMigrationDialog() {
+
+ if (mIsMigrationDialogAlreadyShowed) {
+ return;
+ }
+
+ mIsMigrationDialogAlreadyShowed = true;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(HomeActivity.this)
+ .setTitle(R.string.account_migration_title_dialog)
+ .setMessage(R.string.account_migration_message_dialog)
+ .setIcon(R.drawable.ic_warning)
+ .setCancelable(true)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ onNavigationItemSelected(fMenu.getMenu().findItem(R.id.menuitem_accounts));
+ fMenu.getMenu().findItem(R.id.menuitem_accounts).setChecked(true);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ dialog.dismiss();
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ dialog.dismiss();
+ }
+ });
+ }
+ builder.show();
+ }
+
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
@@ -245,7 +297,7 @@
@Override
protected void onStart() {
- Log.i(TAG, "onStart");
+ Log.d(TAG, "onStart");
if (!PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean("installed", false)) {
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit().putBoolean("installed", true).commit();
copyAssetFolder(getAssets(), "ringtones", getFilesDir().getAbsolutePath() + "/ringtones");
@@ -256,7 +308,7 @@
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
- Log.w(TAG, "onRequestPermissionsResult");
+ Log.d(TAG, "onRequestPermissionsResult");
switch (requestCode) {
case LocalService.PERMISSIONS_REQUEST: {
@@ -318,18 +370,18 @@
}
}
- public void setToolbarState(boolean double_h, int title_res) {
+ public void setToolbarState(boolean doubleHeight, int titleRes) {
mToolbar.setMinimumHeight((int) mToolbarSize);
ViewGroup.LayoutParams toolbarSpacerViewParams = mToolbarSpacerView.getLayoutParams();
- if (double_h) {
+ if (doubleHeight) {
// setting the height of the toolbar spacer with the same height than the toolbar
- toolbarSpacerViewParams.height = (int)mToolbarSize;
+ toolbarSpacerViewParams.height = (int) mToolbarSize;
mToolbarSpacerView.setLayoutParams(toolbarSpacerViewParams);
// setting the toolbar spacer title (hiding the real toolbar title)
- mToolbarSpacerTitle.setText(title_res);
+ mToolbarSpacerTitle.setText(titleRes);
mToolbar.setTitle("");
// the spacer and the action button become visible
@@ -339,7 +391,7 @@
// hide the toolbar spacer and the action button
mToolbarSpacerView.setVisibility(View.GONE);
actionButton.setVisibility(View.GONE);
- mToolbar.setTitle(title_res);
+ mToolbar.setTitle(titleRes);
}
}
@@ -352,19 +404,19 @@
try {
String[] files = assetManager.list(fromAssetPath);
new File(toPath).mkdirs();
- Log.i(TAG, "Creating :" + toPath);
+ Log.d(TAG, "Creating :" + toPath);
boolean res = true;
for (String file : files)
if (file.contains("")) {
- Log.i(TAG, "Copying file :" + fromAssetPath + "/" + file + " to " + toPath + "/" + file);
+ Log.d(TAG, "Copying file :" + fromAssetPath + "/" + file + " to " + toPath + "/" + file);
res &= copyAsset(assetManager, fromAssetPath + "/" + file, toPath + "/" + file);
} else {
- Log.i(TAG, "Copying folder :" + fromAssetPath + "/" + file + " to " + toPath + "/" + file);
+ Log.d(TAG, "Copying folder :" + fromAssetPath + "/" + file + " to " + toPath + "/" + file);
res &= copyAssetFolder(assetManager, fromAssetPath + "/" + file, toPath + "/" + file);
}
return res;
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "Error while copying asset folder", e);
return false;
}
}
@@ -384,7 +436,7 @@
out = null;
return true;
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "Error while copying asset", e);
return false;
}
}
@@ -427,11 +479,11 @@
}
private void popCustomBackStack() {
- FragmentManager fm = getFragmentManager();
- FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt(0);
- fContent = fm.findFragmentByTag(entry.getName());
- for (int i = 0; i < fm.getBackStackEntryCount() - 1; ++i) {
- fm.popBackStack();
+ FragmentManager fragmentManager = getFragmentManager();
+ FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(0);
+ fContent = fragmentManager.findFragmentByTag(entry.getName());
+ for (int i = 0; i < fragmentManager.getBackStackEntryCount() - 1; ++i) {
+ fragmentManager.popBackStack();
}
}
@@ -460,7 +512,7 @@
@Override
public void onServiceConnected(ComponentName className, IBinder s) {
- Log.i(TAG, "onServiceConnected " + className.getClassName());
+ Log.d(TAG, "onServiceConnected " + className.getClassName());
LocalService.LocalBinder binder = (LocalService.LocalBinder) s;
service = binder.getService();
@@ -480,20 +532,20 @@
fMenuHead.setQRCodeListener(mQRCodeClickListener);
}
- FragmentManager fm = getFragmentManager();
- fContent = fm.findFragmentById(R.id.main_frame);
+ FragmentManager fragmentManager = getFragmentManager();
+ fContent = fragmentManager.findFragmentById(R.id.main_frame);
if (fContent == null) {
fContent = new SmartListFragment();
- fm.beginTransaction().replace(R.id.main_frame, fContent, "Home").addToBackStack("Home").commit();
+ fragmentManager.beginTransaction().replace(R.id.main_frame, fContent, "Home").addToBackStack("Home").commit();
} else if (fContent instanceof Refreshable) {
- fm.beginTransaction().replace(R.id.main_frame, fContent).addToBackStack("Home").commit();
+ fragmentManager.beginTransaction().replace(R.id.main_frame, fContent).addToBackStack("Home").commit();
((Refreshable) fContent).refresh();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
- Log.w(TAG, "onServiceDisconnected " + className.getClassName());
+ Log.d(TAG, "onServiceDisconnected " + className.getClassName());
if (fMenuHead != null) {
fMenuHead.setCallbacks(null);
fMenuHead = null;
@@ -514,8 +566,9 @@
switch (requestCode) {
case REQUEST_CODE_PREFERENCES:
case AccountsManagementFragment.ACCOUNT_EDIT_REQUEST:
- if (fMenuHead != null)
+ if (fMenuHead != null) {
fMenuHead.updateAccounts(service.getAccounts());
+ }
break;
case REQUEST_CODE_CALL:
if (resultCode == CallActivity.RESULT_FAILURE) {
@@ -544,24 +597,28 @@
switch (pos.getItemId()) {
case R.id.menuitem_home:
- if (fContent instanceof SmartListFragment)
+ if (fContent instanceof SmartListFragment) {
break;
+ }
- if (getFragmentManager().getBackStackEntryCount() == 1)
+ if (getFragmentManager().getBackStackEntryCount() == 1) {
break;
+ }
popCustomBackStack();
break;
case R.id.menuitem_accounts:
- if (fContent instanceof AccountsManagementFragment)
+ if (fContent instanceof AccountsManagementFragment) {
break;
+ }
fContent = new AccountsManagementFragment();
getFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).replace(R.id.main_frame, fContent, "Accounts").addToBackStack("Accounts").commit();
break;
case R.id.menuitem_about:
- if (fContent instanceof AboutFragment)
+ if (fContent instanceof AboutFragment) {
break;
+ }
fContent = new AboutFragment();
getFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).replace(R.id.main_frame, fContent, "About").addToBackStack("About").commit();
break;
@@ -585,8 +642,9 @@
if (mNavigationDrawer != null) {
mNavigationDrawer.closeDrawers();
}
- if (fContent instanceof SettingsFragment)
+ if (fContent instanceof SettingsFragment) {
return;
+ }
fContent = new SettingsFragment();
getFragmentManager()
.beginTransaction()
@@ -598,12 +656,13 @@
@Override
public void onCallContact(final CallContact c) {
- Log.w(TAG, "onCallContact " + c.toString() + " " + c.getId() + " " + c.getKey());
+ Log.d(TAG, "onCallContact " + c.toString() + " " + c.getId() + " " + c.getKey());
if (c.getPhones().size() > 1) {
final CharSequence numbers[] = new CharSequence[c.getPhones().size()];
int i = 0;
- for (CallContact.Phone p : c.getPhones())
+ for (CallContact.Phone p : c.getPhones()) {
numbers[i++] = p.getNumber().getRawUriString();
+ }
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.choose_number);
@@ -631,8 +690,9 @@
if (c.getPhones().size() > 1) {
final CharSequence numbers[] = new CharSequence[c.getPhones().size()];
int i = 0;
- for (CallContact.Phone p : c.getPhones())
+ for (CallContact.Phone p : c.getPhones()) {
numbers[i++] = p.getNumber().getRawUriString();
+ }
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.choose_number);
@@ -730,17 +790,17 @@
* @return the resulting image
*/
public static Bitmap encodeStringAsQrBitmap(String input, int qrWindowPixels) {
- QRCodeWriter qr_writer = new QRCodeWriter();
- BitMatrix qr_image_matrix;
+ QRCodeWriter qrWriter = new QRCodeWriter();
+ BitMatrix qrImageMatrix;
try {
- qr_image_matrix = qr_writer.encode(input, BarcodeFormat.QR_CODE, qrWindowPixels, qrWindowPixels);
+ qrImageMatrix = qrWriter.encode(input, BarcodeFormat.QR_CODE, qrWindowPixels, qrWindowPixels);
} catch (WriterException e) {
- e.printStackTrace();
+ Log.e(TAG, "Error while encoding QR", e);
return null;
}
- int qrImageWidth = qr_image_matrix.getWidth();
- int qrImageHeight = qr_image_matrix.getHeight();
+ int qrImageWidth = qrImageMatrix.getWidth();
+ int qrImageHeight = qrImageMatrix.getHeight();
int[] pixels = new int[qrImageWidth * qrImageHeight];
final int BLACK = 0x00FFFFFF;
@@ -749,7 +809,7 @@
for (int row = 0; row < qrImageHeight; row++) {
int offset = row * qrImageWidth;
for (int column = 0; column < qrImageWidth; column++) {
- pixels[offset + column] = qr_image_matrix.get(column, row) ? BLACK : WHITE;
+ pixels[offset + column] = qrImageMatrix.get(column, row) ? BLACK : WHITE;
}
}
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 b7d3e23..af94cb2 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
@@ -24,6 +24,7 @@
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.Context;
@@ -41,7 +42,6 @@
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;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
@@ -113,18 +113,18 @@
super.onCreate(savedInstanceState);
}
- private void flipForm(boolean addacc, boolean newacc) {
- mAddAccountLayout.setVisibility(addacc ? View.VISIBLE : View.GONE);
- mNewAccountLayout.setVisibility(newacc ? View.VISIBLE : View.GONE);
+ private void flipForm(boolean addAccount, boolean newAccount) {
+ mAddAccountLayout.setVisibility(addAccount ? View.VISIBLE : View.GONE);
+ mNewAccountLayout.setVisibility(newAccount ? View.VISIBLE : View.GONE);
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (newacc) {
+ if (newAccount) {
mRingPassword.requestFocus();
imm.showSoftInput(mRingPassword, InputMethodManager.SHOW_IMPLICIT);
- } else if (addacc) {
+ } else if (addAccount) {
mRingPin.requestFocus();
imm.showSoftInput(mRingPin, InputMethodManager.SHOW_IMPLICIT);
}
- if (addacc || newacc) {
+ if (addAccount || newAccount) {
mSipFormLinearLayout.setVisibility(View.GONE);
}
}
@@ -137,44 +137,18 @@
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);
+ final Button ringCreateBtn = (Button) inflatedView.findViewById(R.id.ring_create_btn);
+ final Button ringAddBtn = (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()));
+ Log.d(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
if (actionId == EditorInfo.IME_ACTION_NEXT)
return checkPassword(v, null);
return false;
@@ -186,17 +160,17 @@
if (!hasFocus) {
checkPassword((TextView) v, null);
} else {
- //alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ // nothing to be done here
}
}
});
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()));
+ Log.d(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();
+ ringCreateBtn.callOnClick();
return true;
}
}
@@ -206,16 +180,16 @@
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()));
+ Log.d(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
if (actionId == EditorInfo.IME_ACTION_DONE) {
- ring_add_btn.callOnClick();
+ ringAddBtn.callOnClick();
return true;
}
return false;
}
});
- ring_create_btn.setOnClickListener(new View.OnClickListener() {
+ ringCreateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mNewAccountLayout.getVisibility() == View.GONE) {
@@ -223,14 +197,13 @@
} 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());
+ initAccountCreation(null, null, mRingPassword.getText().toString());
}
}
}
});
- ring_add_btn.setOnClickListener(new View.OnClickListener() {
+ ringAddBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mAddAccountLayout.getVisibility() == View.GONE) {
@@ -253,13 +226,7 @@
return true;
}
});
- /*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) {
@@ -271,12 +238,6 @@
attemptCreation();
}
});
- /*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);
@@ -406,10 +367,11 @@
return cursor.getString(column_index);
}
} catch (Exception e) {
- Log.w(TAG, "Can't find data column", e);
+ Log.e(TAG, "Can't find data column", e);
} finally {
- if (cursor != null)
+ if (cursor != null) {
cursor.close();
+ }
}
return null;
}
@@ -484,12 +446,12 @@
// Technically the column stores an int, but cursor.getString()
// will do the conversion automatically.
size = cursor.getString(sizeIndex);
- Log.i(TAG, "Size: " + size);
+ Log.d(TAG, "Size: " + size);
return cursor.getInt(sizeIndex);
} else {
size = "Unknown";
}
- Log.i(TAG, "Size: " + size);
+ Log.d(TAG, "Size: " + size);
}
} finally {
@@ -500,7 +462,7 @@
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();
+ Toast.makeText(getActivity(), R.string.account_creation_file_too_big, Toast.LENGTH_LONG).show();
throw new IOException("File is too big");
}
copy(new InputStreamReader(getActivity().getContentResolver().openInputStream(uri)), new FileWriter(outPath));
@@ -520,11 +482,11 @@
showImportDialog();
} catch (IOException e) {
e.printStackTrace();
- Toast.makeText(getActivity(), "Can't read " + data.getData(), Toast.LENGTH_LONG).show();
+ Toast.makeText(getActivity(), getContext().getString(R.string.account_cannot_read, data.getData()), Toast.LENGTH_LONG).show();
}
- }
- else
+ } else {
showImportDialog();
+ }
}
break;
}
@@ -603,20 +565,22 @@
try {
ret = mCallbacks.getRemoteService().importAccounts(args[0], args[1]);
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Error while importing account", e);
}
return ret;
}
protected void onPostExecute(Integer ret) {
- if (loading_dialog != null)
+ if (loading_dialog != null) {
loading_dialog.dismiss();
- if (ret == 0)
+ }
+ if (ret == 0) {
getActivity().finish();
- else
+ } else {
new AlertDialog.Builder(getActivity()).setTitle(R.string.import_failed_dialog_title)
.setMessage(R.string.import_failed_dialog_msg)
.setPositiveButton(android.R.string.ok, null).show();
+ }
}
}
@@ -709,7 +673,7 @@
}
@SuppressWarnings("unchecked")
- private void initAccountCreation(String new_username, String pin, String password) {
+ private void initAccountCreation(String newUsername, String pin, String password) {
try {
HashMap<String, String> accountDetails = (HashMap<String, String>) mCallbacks.getRemoteService().getAccountTemplate(mAccountType);
accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_TYPE, mAccountType);
@@ -725,8 +689,9 @@
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())
+ if (password != null && !password.isEmpty()) {
accountDetails.put(AccountDetailBasic.CONFIG_ARCHIVE_PASSWORD, password);
+ }
if (pin != null && !pin.isEmpty()) {
accountDetails.put(AccountDetailBasic.CONFIG_ARCHIVE_PIN, pin);
}
@@ -742,8 +707,8 @@
}
} catch (RemoteException e) {
- Toast.makeText(getActivity(), "Error creating account", Toast.LENGTH_SHORT).show();
- e.printStackTrace();
+ Toast.makeText(getActivity(), R.string.account_creation_error, Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "Error while creating account", e);
}
}
@@ -771,83 +736,13 @@
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;
+ CreateAccountTask(String regUsername) {
+ Log.d(TAG, "CreateAccountTask ");
+ username = regUsername;
}
@Override
@@ -863,13 +758,13 @@
@SafeVarargs
@Override
protected final String doInBackground(HashMap<String, String>... accs) {
- final Account acc = mCallbacks.getService().createAccount(accs[0]);
- acc.stateListener = new Account.OnStateChangedListener() {
+ final Account account = mCallbacks.getService().createAccount(accs[0]);
+ account.stateListener = new Account.OnStateChangedListener() {
@Override
public void stateChanged(String state, int code) {
- Log.w(TAG, "stateListener -> stateChanged " + state + " " + code);
+ Log.d(TAG, "stateListener -> stateChanged " + state + " " + code);
if (!AccountDetailVolatile.STATE_INITIALIZING.contentEquals(state)) {
- acc.stateListener = null;
+ account.stateListener = null;
if (progress != null) {
progress.dismiss();
progress = null;
@@ -884,17 +779,16 @@
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.");
+ dialog.setTitle(R.string.account_cannot_be_found_title)
+ .setMessage(R.string.account_cannot_be_found_message);
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.");
+ dialog.setTitle(R.string.account_no_network_title)
+ .setMessage(R.string.account_no_network_message);
break;
default:
- dialog.setTitle("Account device added")
- .setMessage("You have successfully setup your Ring account on this device.");
+ dialog.setTitle(R.string.account_device_added_title)
+ .setMessage(R.string.account_device_added_message);
success = true;
break;
}
@@ -911,18 +805,19 @@
}
}
};
- Log.w(TAG, "Account created, registering " + username);
- return acc.getAccountID();
+ Log.d(TAG, "Account created, registering " + username);
+ return account.getAccountID();
}
}
- private void createNewAccount(HashMap<String, String> accountDetails, String register_name) {
- if (creatingAccount)
+ private void createNewAccount(HashMap<String, String> accountDetails, String registerName) {
+ if (creatingAccount) {
return;
+ }
creatingAccount = true;
//noinspection unchecked
- new CreateAccountTask(register_name).execute(accountDetails);
+ new CreateAccountTask(registerName).execute(accountDetails);
}
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java
new file mode 100644
index 0000000..4dacca0
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountMigrationFragment.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ * Author: Adrien Béraud <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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package cx.ring.fragments;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import java.util.HashMap;
+
+import cx.ring.R;
+import cx.ring.model.account.Account;
+import cx.ring.model.account.AccountDetailBasic;
+import cx.ring.model.account.AccountDetailVolatile;
+import cx.ring.service.IDRingService;
+import cx.ring.service.LocalService;
+
+public class AccountMigrationFragment extends Fragment {
+ static final String TAG = AccountMigrationFragment.class.getSimpleName();
+
+ public static final String ACCOUNT_ID = "ACCOUNT_ID";
+
+ private String mAccountId;
+
+ // UI references.
+ private EditText mRingPassword;
+ private EditText mRingPasswordRepeat;
+
+ private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
+ private boolean migratingAccount = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ final View inflatedView = inflater.inflate(R.layout.frag_account_migration, parent, false);
+
+ mRingPassword = (EditText) inflatedView.findViewById(R.id.ring_password);
+ mRingPasswordRepeat = (EditText) inflatedView.findViewById(R.id.ring_password_repeat);
+
+ final Button ringMigrateButton = (Button) inflatedView.findViewById(R.id.ring_migrate_btn);
+
+ mRingPassword.setOnEditorActionListener(new OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.d(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ return actionId == EditorInfo.IME_ACTION_NEXT && checkPassword(v, null);
+ }
+ });
+ mRingPassword.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ checkPassword((TextView) v, null);
+ }
+ }
+ });
+ mRingPasswordRepeat.setOnEditorActionListener(new OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ Log.d(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+ if (actionId == EditorInfo.IME_ACTION_DONE
+ && mRingPassword.getText().length() != 0
+ && !checkPassword(mRingPassword, v)) {
+ ringMigrateButton.callOnClick();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ ringMigrateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (!checkPassword(mRingPassword, mRingPasswordRepeat)) {
+ initAccountMigration(mRingPassword.getText().toString());
+ }
+ }
+ });
+
+ return inflatedView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (getArguments() != null) {
+ mAccountId = getArguments().getString(ACCOUNT_ID);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.update_account);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ if (!(activity instanceof LocalService.Callbacks)) {
+ throw new IllegalStateException("Activity must implement fragment's callbacks.");
+ }
+
+ mCallbacks = (LocalService.Callbacks) activity;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initAccountMigration(String password) {
+ if (migratingAccount) {
+ return;
+ }
+
+ migratingAccount = true;
+
+ new MigrateAccountTask(mAccountId, password).execute();
+ }
+
+ 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));
+ pwd.requestFocus();
+ 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));
+ confirm.requestFocus();
+ error = true;
+ } else {
+ confirm.setError(null);
+ }
+ }
+ return error;
+ }
+
+ private class MigrateAccountTask extends AsyncTask<HashMap<String, String>, Void, String> {
+ private ProgressDialog progress = null;
+ private final String mAccountId;
+ private final String mPassword;
+
+ MigrateAccountTask(String accountId, String password) {
+ Log.d(TAG, "MigrateAccountTask");
+ mAccountId = accountId;
+ mPassword = password;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ progress = new ProgressDialog(getActivity());
+ progress.setTitle(R.string.dialog_wait_update);
+ progress.setMessage(getString(R.string.dialog_wait_update_details));
+ progress.setCancelable(false);
+ progress.setCanceledOnTouchOutside(false);
+ progress.show();
+ }
+
+ @SafeVarargs
+ @Override
+ protected final String doInBackground(HashMap<String, String>... accs) {
+
+ final Account account = mCallbacks.getService().getAccount(mAccountId);
+ final IDRingService remote = mCallbacks.getService().getRemoteService();
+ if (account == null || remote == null) {
+ Log.e(TAG, "Error updating account, no account or remote service");
+ return null;
+ }
+
+ account.stateListener = new Account.OnStateChangedListener() {
+ @Override
+ public void stateChanged(String state, int code) {
+ Log.d(TAG, "stateListener -> stateChanged " + state + " " + code);
+ if (!AccountDetailVolatile.STATE_INITIALIZING.contentEquals(state)) {
+ if (progress != null) {
+ progress.dismiss();
+ progress = null;
+ }
+ AlertDialog.Builder dialogBuilder = new android.support.v7.app.AlertDialog.Builder(getActivity());
+ dialogBuilder.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:
+ dialogBuilder.setTitle(R.string.account_cannot_be_found_title)
+ .setMessage(R.string.account_cannot_be_found_message);
+ break;
+ case AccountDetailVolatile.STATE_ERROR_NETWORK:
+ dialogBuilder.setTitle(R.string.account_no_network_title)
+ .setMessage(R.string.account_no_network_message);
+ break;
+ default:
+ dialogBuilder.setTitle(R.string.account_device_updated_title)
+ .setMessage(R.string.account_device_updated_message);
+ success = true;
+ account.stateListener = null;
+ break;
+ }
+ AlertDialog dialogSuccess = dialogBuilder.show();
+ if (success) {
+ dialogSuccess.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ getActivity().setResult(Activity.RESULT_OK, new Intent());
+ getActivity().finish();
+ }
+ });
+ }
+ }
+ }
+ };
+
+ HashMap<String, String> details = account.getDetails();
+ details.put(AccountDetailBasic.CONFIG_ARCHIVE_PASSWORD, mPassword);
+
+ try {
+ remote.setAccountDetails(account.getAccountID(), details);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while setting ARCHIVE_PASSWORD", e);
+ }
+
+ return account.getAccountID();
+ }
+ }
+}
\ 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 4fd1a0e..d323620 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
@@ -34,10 +34,20 @@
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.util.Log;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
import cx.ring.R;
import cx.ring.client.AccountEditionActivity;
@@ -48,9 +58,6 @@
import cx.ring.service.LocalService;
import cx.ring.views.dragsortlv.DragSortListView;
-import java.util.ArrayList;
-import java.util.List;
-
public class AccountsManagementFragment extends Fragment implements HomeActivity.Refreshable {
static final String TAG = AccountsManagementFragment.class.getSimpleName();
@@ -124,7 +131,12 @@
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int pos, long arg3) {
- launchAccountEditActivity(mAccountsAdapter.getItem(pos));
+ Account selectedAccount = mAccountsAdapter.getItem(pos);
+ if (selectedAccount.needsMigration()) {
+ launchAccountMigrationActivity(mAccountsAdapter.getItem(pos));
+ } else {
+ launchAccountEditActivity(mAccountsAdapter.getItem(pos));
+ }
}
});
}
@@ -150,7 +162,7 @@
}
private void launchAccountEditActivity(Account acc) {
- Log.i(TAG, "Launch account edit activity");
+ Log.d(TAG, "Launch account edit activity");
Intent intent = new Intent()
.setClass(getActivity(), AccountEditionActivity.class)
@@ -159,6 +171,15 @@
startActivityForResult(intent, ACCOUNT_EDIT_REQUEST);
}
+ private void launchAccountMigrationActivity(Account acc) {
+ Log.d(TAG, "Launch account migration activity");
+
+ Intent intent = new Intent()
+ .setClass(getActivity(), AccountWizard.class)
+ .setData(Uri.withAppendedPath(AccountEditionActivity.CONTENT_URI, acc.getAccountID()));
+ startActivityForResult(intent, ACCOUNT_EDIT_REQUEST);
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@@ -225,12 +246,12 @@
entryView.handle = (ImageView) rowView.findViewById(R.id.drag_handle);
entryView.alias = (TextView) rowView.findViewById(R.id.account_alias);
entryView.host = (TextView) rowView.findViewById(R.id.account_host);
- entryView.loading_indicator = rowView.findViewById(R.id.loading_indicator);
- entryView.error_indicator = (ImageView) rowView.findViewById(R.id.error_indicator);
+ entryView.loadingIndicator = rowView.findViewById(R.id.loading_indicator);
+ entryView.errorIndicator = (ImageView) rowView.findViewById(R.id.error_indicator);
entryView.enabled = (CheckBox) rowView.findViewById(R.id.account_checked);
- entryView.error_indicator.setColorFilter(mContext.getResources().getColor(R.color.error_red));
- entryView.error_indicator.setVisibility(View.GONE);
- entryView.loading_indicator.setVisibility(View.GONE);
+ entryView.errorIndicator.setColorFilter(mContext.getResources().getColor(R.color.error_red));
+ entryView.errorIndicator.setVisibility(View.GONE);
+ entryView.loadingIndicator.setVisibility(View.GONE);
rowView.setTag(entryView);
} else {
entryView = (AccountView) rowView.getTag();
@@ -238,13 +259,15 @@
final Account item = accounts.get(pos);
entryView.alias.setText(item.getAlias());
+ entryView.host.setTextColor(getResources().getColor(R.color.text_color_secondary));
- if (item.isIP2IP())
+ if (item.isIP2IP()) {
entryView.host.setText(item.getRegistrationState());
- else if (item.isSip())
+ } else if (item.isSip()) {
entryView.host.setText(item.getHost() + " - " + item.getRegistrationState());
- else
+ } else {
entryView.host.setText(item.getBasicDetails().getDetailString(AccountDetailBasic.CONFIG_ACCOUNT_USERNAME));
+ }
entryView.enabled.setChecked(item.isEnabled());
entryView.enabled.setOnClickListener(new OnClickListener() {
@@ -255,7 +278,7 @@
try {
mCallbacks.getService().getRemoteService().setAccountDetails(item.getAccountID(), item.getDetails());
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Error while getting Account details", e);
}
}
});
@@ -263,25 +286,31 @@
if (item.isEnabled()) {
if (item.isTrying()) {
- entryView.error_indicator.setVisibility(View.GONE);
- entryView.loading_indicator.setVisibility(View.VISIBLE);
+ entryView.errorIndicator.setVisibility(View.GONE);
+ entryView.loadingIndicator.setVisibility(View.VISIBLE);
+ } else if (item.needsMigration()) {
+ entryView.host.setText(R.string.account_update_needed);
+ entryView.host.setTextColor(Color.RED);
+ entryView.errorIndicator.setImageResource(R.drawable.ic_warning);
+ entryView.errorIndicator.setColorFilter(Color.RED);
+ entryView.errorIndicator.setVisibility(View.VISIBLE);
} else if (item.isInError()) {
- entryView.error_indicator.setImageResource(R.drawable.ic_error_white_24dp);
- entryView.error_indicator.setColorFilter(Color.RED);
- entryView.error_indicator.setVisibility(View.VISIBLE);
- entryView.loading_indicator.setVisibility(View.GONE);
+ entryView.errorIndicator.setImageResource(R.drawable.ic_error_white_24dp);
+ entryView.errorIndicator.setColorFilter(Color.RED);
+ entryView.errorIndicator.setVisibility(View.VISIBLE);
+ entryView.loadingIndicator.setVisibility(View.GONE);
} else if (!item.isRegistered()) {
- entryView.error_indicator.setImageResource(R.drawable.ic_network_disconnect_black_24dp);
- entryView.error_indicator.setColorFilter(Color.BLACK);
- entryView.error_indicator.setVisibility(View.VISIBLE);
- entryView.loading_indicator.setVisibility(View.GONE);
+ entryView.errorIndicator.setImageResource(R.drawable.ic_network_disconnect_black_24dp);
+ entryView.errorIndicator.setColorFilter(Color.BLACK);
+ entryView.errorIndicator.setVisibility(View.VISIBLE);
+ entryView.loadingIndicator.setVisibility(View.GONE);
} else {
- entryView.error_indicator.setVisibility(View.GONE);
- entryView.loading_indicator.setVisibility(View.GONE);
+ entryView.errorIndicator.setVisibility(View.GONE);
+ entryView.loadingIndicator.setVisibility(View.GONE);
}
} else {
- entryView.error_indicator.setVisibility(View.GONE);
- entryView.loading_indicator.setVisibility(View.GONE);
+ entryView.errorIndicator.setVisibility(View.GONE);
+ entryView.loadingIndicator.setVisibility(View.GONE);
}
return rowView;
@@ -296,13 +325,13 @@
public ImageView handle;
public TextView alias;
public TextView host;
- public View loading_indicator;
- public ImageView error_indicator;
+ public View loadingIndicator;
+ public ImageView errorIndicator;
public CheckBox enabled;
}
public void replaceAll(List<Account> results) {
- Log.i(TAG, "AccountsAdapter replaceAll " + results.size());
+ Log.d(TAG, "AccountsAdapter replaceAll " + results.size());
accounts.clear();
accounts.addAll(results);
notifyDataSetChanged();
@@ -310,8 +339,9 @@
private List<String> getAccountOrder() {
ArrayList<String> order = new ArrayList<>(accounts.size());
- for (Account acc : accounts)
+ for (Account acc : accounts) {
order.add(acc.getAccountID());
+ }
return order;
}
@@ -329,8 +359,9 @@
public void refresh() {
LocalService service = mCallbacks.getService();
View v = getView();
- if (service == null || v == null)
+ if (service == null || v == null) {
return;
+ }
mAccountsAdapter.replaceAll(service.getAccounts());
if (mAccountsAdapter.isEmpty()) {
mDnDListView.setEmptyView(v.findViewById(R.id.empty_account_list));
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 fba2ee5..d722e00 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
@@ -30,7 +30,7 @@
import java.util.Map;
public class Account extends java.util.Observable {
- private static final String TAG = "Account";
+ private static final String TAG = Account.class.getName();
private final String accountID;
private AccountDetailBasic basicDetails = null;
@@ -58,8 +58,9 @@
advancedDetails = new AccountDetailAdvanced(details);
srtpDetails = new AccountDetailSrtp(details);
tlsDetails = new AccountDetailTls(details);
- if (volatile_details != null)
+ if (volatile_details != null) {
volatileDetails = new AccountDetailVolatile(volatile_details);
+ }
setCredentials(credentials);
}
@@ -97,9 +98,11 @@
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);
}
@@ -133,15 +136,17 @@
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())
+ String codeStr = volatileDetails.getDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE);
+ if (codeStr == null || codeStr.isEmpty()) {
return 0;
- return Integer.parseInt(code_str);
+ }
+ return Integer.parseInt(codeStr);
}
public void setRegistrationState(String registered_state, int code) {
- Log.i(TAG, "setRegistrationState " + registered_state + " " + code);
+ Log.d(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));
@@ -179,6 +184,12 @@
basicDetails.setDetailString(AccountDetailBasic.CONFIG_ACCOUNT_ALIAS, alias);
}
+ public void setPassword(String password) {
+ Map<String, String> details = basicDetails.getDetailsHashMap();
+ details.put(AccountDetailBasic.CONFIG_ACCOUNT_PASSWORD, password);
+ setBasicDetails(details);
+ }
+
public AccountDetailBasic getBasicDetails() {
return basicDetails;
}
@@ -294,13 +305,17 @@
}
public String getShareURI() {
- String share_uri;
+ String shareUri;
if (isRing()) {
- share_uri = getBasicDetails().getUsername();
+ shareUri = getBasicDetails().getUsername();
} else {
- share_uri = getBasicDetails().getUsername() + "@" + getBasicDetails().getHostname();
+ shareUri = getBasicDetails().getUsername() + "@" + getBasicDetails().getHostname();
}
- return share_uri;
+ return shareUri;
+ }
+
+ public boolean needsMigration() {
+ return getRegistrationState().contentEquals(AccountDetailVolatile.STATE_NEED_MIGRATION);
}
}
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 26718e4..a91a67e 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
@@ -1,23 +1,23 @@
/**
* Copyright (C) 2004-2016 Savoir-faire Linux Inc.
- *
- * Author: Alexandre Savard <alexandre.savard@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.
- * If you own a pjsip commercial license you can also redistribute it
- * and/or modify it under the terms of the GNU Lesser General Public License
- * as an android library.
- *
- * 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/>.
+ * <p>
+ * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
+ * <p>
+ * 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.
+ * If you own a pjsip commercial license you can also redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public License
+ * as an android library.
+ * <p>
+ * 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.
+ * <p>
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cx.ring.model.account;
@@ -35,32 +35,34 @@
public static final String CONFIG_ACCOUNT_REGISTRATION_STATE_CODE = "Account.registrationCode";
public static final String CONFIG_ACCOUNT_REGISTRATION_STATE_DESC = "Account.registrationDescription";
- public static final String STATE_REGISTERED = "REGISTERED";
- public static final String STATE_READY = "READY";
- public static final String STATE_UNREGISTERED = "UNREGISTERED";
- public static final String STATE_TRYING = "TRYING";
- public static final String STATE_ERROR = "ERROR";
- public static final String STATE_ERROR_GENERIC = "ERROR_GENERIC";
- public static final String STATE_ERROR_AUTH = "ERROR_AUTH";
- public static final String STATE_ERROR_NETWORK = "ERROR_NETWORK";
- public static final String STATE_ERROR_HOST = "ERROR_HOST";
- public static final String STATE_ERROR_CONF_STUN = "ERROR_CONF_STUN";
- public static final String STATE_ERROR_EXIST_STUN = "ERROR_EXIST_STUN";
- 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";
+ public static final String STATE_REGISTERED = "REGISTERED";
+ public static final String STATE_READY = "READY";
+ public static final String STATE_UNREGISTERED = "UNREGISTERED";
+ public static final String STATE_TRYING = "TRYING";
+ public static final String STATE_ERROR = "ERROR";
+ public static final String STATE_ERROR_GENERIC = "ERROR_GENERIC";
+ public static final String STATE_ERROR_AUTH = "ERROR_AUTH";
+ public static final String STATE_ERROR_NETWORK = "ERROR_NETWORK";
+ public static final String STATE_ERROR_HOST = "ERROR_HOST";
+ public static final String STATE_ERROR_CONF_STUN = "ERROR_CONF_STUN";
+ public static final String STATE_ERROR_EXIST_STUN = "ERROR_EXIST_STUN";
+ 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";
+ public static final String STATE_NEED_MIGRATION = "ERROR_NEED_MIGRATION";
private final ArrayList<PreferenceEntry> privateArray = new ArrayList<>();
public AccountDetailVolatile() {
}
+
public AccountDetailVolatile(Map<String, String> pref) {
for (String key : pref.keySet()) {
- PreferenceEntry p = new PreferenceEntry(key);
- p.mValue = pref.get(key);
+ PreferenceEntry preferenceEntry = new PreferenceEntry(key);
+ preferenceEntry.mValue = pref.get(key);
- privateArray.add(p);
+ privateArray.add(preferenceEntry);
}
}
@@ -71,9 +73,9 @@
public ArrayList<String> getValuesOnly() {
ArrayList<String> valueList = new ArrayList<>();
- for (PreferenceEntry p : privateArray) {
- Log.i(TAG, "" + p.mValue);
- valueList.add(p.mValue);
+ for (PreferenceEntry preferenceEntry : privateArray) {
+ Log.d(TAG, "" + preferenceEntry.mValue);
+ valueList.add(preferenceEntry.mValue);
}
return valueList;
@@ -82,8 +84,8 @@
public HashMap<String, String> getDetailsHashMap() {
HashMap<String, String> map = new HashMap<>();
- for (PreferenceEntry p : privateArray) {
- map.put(p.mKey, p.mValue);
+ for (PreferenceEntry preferenceEntry : privateArray) {
+ map.put(preferenceEntry.mKey, preferenceEntry.mValue);
}
return map;
@@ -92,9 +94,9 @@
public String getDetailString(String key) {
String value = "";
- for (PreferenceEntry p : privateArray) {
- if (p.mKey.equals(key)) {
- value = p.mValue;
+ for (PreferenceEntry preferenceEntry : privateArray) {
+ if (preferenceEntry.mKey.equals(key)) {
+ value = preferenceEntry.mValue;
return value;
}
}
@@ -102,9 +104,9 @@
}
public void setDetailString(String key, String newValue) {
- for (PreferenceEntry p : privateArray) {
- if (p.mKey.equals(key)) {
- p.mValue = newValue;
+ for (PreferenceEntry preferenceEntry : privateArray) {
+ if (preferenceEntry.mKey.equals(key)) {
+ preferenceEntry.mValue = newValue;
}
}
diff --git a/ring-android/app/src/main/res/layout/activity_wizard.xml b/ring-android/app/src/main/res/layout/activity_wizard.xml
index e4dc955..37d0c24 100644
--- a/ring-android/app/src/main/res/layout/activity_wizard.xml
+++ b/ring-android/app/src/main/res/layout/activity_wizard.xml
@@ -1,4 +1,4 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -8,9 +8,6 @@
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
android:background="@color/color_primary_light"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
@@ -24,7 +21,6 @@
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/main_toolbar" />
+ android:layout_height="match_parent" />
-</RelativeLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_account_migration.xml b/ring-android/app/src/main/res/layout/frag_account_migration.xml
new file mode 100644
index 0000000..c4fd7d0
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/frag_account_migration.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+
+Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ Romain Bertozzi <romain.bertozzi@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, write to the Free Software
+ 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="wrap_content"
+ android:background="#eeeeee"
+ tools:context=".client.AccountWizard">
+
+ <cx.ring.views.BoundedLinearLayout
+ style="@style/AccountFormContainer"
+ android:layout_gravity="center_horizontal"
+ android:animateLayoutChanges="false"
+ android:descendantFocusability="beforeDescendants"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical"
+ app:bounded_width="320dp">
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/ring_card_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ 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
+ android:id="@+id/imageView6"
+ 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/app_name"
+ android:src="@drawable/ring_logo_48dp" />
+
+ <TextView
+ android:id="@+id/ring_acc_title_txt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:layout_toLeftOf="@+id/imageView6"
+ android:layout_toStartOf="@+id/imageView6"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="24dp"
+ android:text="@string/ring_account"
+ android:textColor="@color/text_color_primary"
+ android:textSize="24sp" />
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/ring_acc_title_txt"
+ android:paddingBottom="24dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp"
+ android:text="@string/account_migration"
+ android:textColor="@color/text_color_primary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/textView"
+ android:background="@color/color_primary_light"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/newAccountLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/color_primary_light"
+ android:orientation="vertical">
+
+ <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:imeOptions="actionNext"
+ android:inputType="textPassword" />
+
+ <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="update"
+ android:inputType="textPassword" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/ring_migrate_btn"
+ style="?attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/update_account"
+ android:textColor="@color/text_color_primary_dark" />
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ </android.support.v7.widget.CardView>
+
+ </cx.ring.views.BoundedLinearLayout>
+
+</ScrollView>
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index e762524..9149e88 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -166,5 +166,6 @@
<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>
+ <string name="account_device_updated_message">You have successfully updated your Ring account.</string>
</resources>
diff --git a/ring-android/app/src/main/res/values/strings_account.xml b/ring-android/app/src/main/res/values/strings_account.xml
index 6231b6f..bc587f6 100644
--- a/ring-android/app/src/main/res/values/strings_account.xml
+++ b/ring-android/app/src/main/res/values/strings_account.xml
@@ -30,6 +30,8 @@
<string name="error_field_required">This field is required</string>
<string name="dialog_wait_create">Adding account</string>
<string name="dialog_wait_create_details">Please wait while your new account is added…</string>
+ <string name="dialog_wait_update">Updating account</string>
+ <string name="dialog_wait_update_details">Please wait while your new account is updated…</string>
<string name="dialog_warn_ip2ip_account_title">Create empty SIP account ?</string>
<string name="dialog_warn_ip2ip_account_message">You are about to create a SIP account with no valid hostname.
@@ -168,6 +170,21 @@
<string name="account_enter_password">Enter password</string>
<string name="account_share_body">Contact me using %1$s on the Ring distributed communication platform: http://ring.cx</string>
<string name="account_contact_me">Contact me on Ring !</string>
-
+ <string name="update_account">Update account</string>
+ <string name="account_migration">Your Ring account can now be shared between all your devices. All you need to do is provide a password.</string>
+ <string name="ring_account">Ring account</string>
+ <string name="account_migration_title_dialog">Account migration</string>
+ <string name="account_migration_message_dialog">In order to be usable on multiple devices, your accounts need to be updated. Do you want to go the Account management screen to perform this operation ?</string>
+ <string name="account_update_needed">Update needed</string>
+ <string name="account_creation_file_too_big">File is too big</string>
+ <string name="account_cannot_read">Can\'t read %1$s</string>
+ <string name="account_creation_error">Error creating account</string>
+ <string name="account_cannot_be_found_title">Can\'t find account</string>
+ <string name="account_cannot_be_found_message">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.</string>
+ <string name="account_no_network_title">Can\'t connect to the network</string>
+ <string name="account_no_network_message">Could not add account because Ring coudn\'t connect to the distributed network. Check your device connectivity.</string>
+ <string name="account_device_added_title">Account device added</string>
+ <string name="account_device_added_message">You have successfully setup your Ring account on this device.</string>
+ <string name="account_device_updated_title">Account device updated</string>
</resources>