Commit fb7f9749 authored by Ricki Hirner's avatar Ricki Hirner 🐑

Allow renaming of accounts

* allow renaming of accounts
* always open AccountActivity, even if there are no services (so that users can delete the account from within DAVdroid)
parent 900f1fd8
Pipeline #4977048 passed with stage
in 16 minutes and 28 seconds
......@@ -8,17 +8,16 @@
package at.bitfire.davdroid.model;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import java.util.logging.Level;
import aQute.service.reporter.Messages;
import at.bitfire.davdroid.App;
import lombok.Cleanup;
......@@ -186,4 +185,11 @@ public class ServiceDB {
}
}
public static void onRenameAccount(@NonNull SQLiteDatabase db, @NonNull String oldName, @NonNull String newName) {
ContentValues values = new ContentValues(1);
values.put(Services.ACCOUNT_NAME, newName);
db.update(Services._TABLE, values, Services.ACCOUNT_NAME + "=?", new String[] { oldName });
}
}
......@@ -9,6 +9,7 @@ package at.bitfire.davdroid.resource;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
......@@ -16,6 +17,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.support.annotation.NonNull;
......@@ -226,4 +228,14 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
}
}
// HELPERS
public static void onRenameAccount(@NonNull ContentResolver resolver, @NonNull String oldName, @NonNull String newName) throws RemoteException {
@Cleanup("release") ContentProviderClient client = resolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
ContentValues values = new ContentValues(1);
values.put(RawContacts.ACCOUNT_NAME, newName);
client.update(RawContacts.CONTENT_URI, values, RawContacts.ACCOUNT_NAME + "=?", new String[] { oldName });
}
}
......@@ -251,4 +251,5 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
return new LocalCalendar[size];
}
}
}
......@@ -9,6 +9,8 @@
package at.bitfire.davdroid.resource;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
......@@ -164,4 +166,15 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
return new LocalTaskList[size];
}
}
// HELPERS
public static void onRenameAccount(@NonNull ContentResolver resolver, @NonNull String oldName, @NonNull String newName) throws RemoteException {
@Cleanup("release") ContentProviderClient client = resolver.acquireContentProviderClient(TaskProvider.ProviderName.OpenTasks.authority);
ContentValues values = new ContentValues(1);
values.put(Tasks.ACCOUNT_NAME, newName);
client.update(Tasks.getContentUri(TaskProvider.ProviderName.OpenTasks.authority), values, Tasks.ACCOUNT_NAME + "=?", new String[] { oldName });
}
}
......@@ -14,6 +14,7 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Dialog;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.ComponentName;
......@@ -32,10 +33,12 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
......@@ -50,6 +53,7 @@ import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
......@@ -72,6 +76,8 @@ import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import at.bitfire.davdroid.resource.LocalAddressBook;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;
......@@ -136,6 +142,14 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem itemRename = menu.findItem(R.id.rename_account);
// renameAccount is available for API level 21+
itemRename.setVisible(Build.VERSION.SDK_INT >= 21);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
......@@ -147,6 +161,9 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
intent.putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account);
startActivity(intent);
break;
case R.id.rename_account:
RenameAccountFragment.newInstance(account).show(getSupportFragmentManager(), null);
break;
case R.id.delete_account:
new AlertDialog.Builder(AccountActivity.this)
.setIcon(R.drawable.ic_error_dark)
......@@ -172,7 +189,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
Intent intent;
switch (item.getItemId()) {
case R.id.refresh_address_books:
if (accountInfo.carddav != null) {
if (accountInfo != null && accountInfo.carddav != null) {
intent = new Intent(this, DavService.class);
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.carddav.id);
......@@ -185,7 +202,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
startActivity(intent);
break;
case R.id.refresh_calendars:
if (accountInfo.caldav != null) {
if (accountInfo != null && accountInfo.caldav != null) {
intent = new Intent(this, DavService.class);
intent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
intent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, accountInfo.caldav.id);
......@@ -298,12 +315,6 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
public void onLoadFinished(Loader<AccountInfo> loader, final AccountInfo info) {
accountInfo = info;
if (accountInfo == null) {
// account doesn't exist anymore
finish();
return;
}
CardView card = (CardView)findViewById(R.id.carddav);
if (info.carddav != null) {
ProgressBar progress = (ProgressBar)findViewById(R.id.carddav_refreshing);
......@@ -415,10 +426,6 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
Services.ACCOUNT_NAME + "=?", new String[] { account.name },
null, null, null);
if (cursor.getCount() == 0)
// no services, account not useable
return null;
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String service = cursor.getString(1);
......@@ -545,6 +552,88 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
}
/* DIALOG FRAGMENTS */
public static class RenameAccountFragment extends DialogFragment {
private final static String ARG_ACCOUNT = "account";
static RenameAccountFragment newInstance(@NonNull Account account) {
RenameAccountFragment fragment = new RenameAccountFragment();
Bundle args = new Bundle(1);
args.putParcelable(ARG_ACCOUNT, account);
fragment.setArguments(args);
return fragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Account oldAccount = getArguments().getParcelable(ARG_ACCOUNT);
final EditText editText = new EditText(getContext());
editText.setText(oldAccount.name);
return new AlertDialog.Builder(getContext())
.setTitle(R.string.account_rename)
.setMessage(R.string.account_rename_new_name)
.setView(editText)
.setPositiveButton(R.string.account_rename_rename, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final String newName = editText.getText().toString();
if (newName.equals(oldAccount.name))
return;
final AccountManager accountManager = AccountManager.get(getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
accountManager.renameAccount(oldAccount, newName,
new AccountManagerCallback<Account>() {
@Override
public void run(AccountManagerFuture<Account> future) {
App.log.info("Updating account name references");
// cancel running synchronization
ContentResolver.cancelSync(oldAccount, null);
// update account name references in database
@Cleanup OpenHelper dbHelper = new OpenHelper(getContext());
ServiceDB.onRenameAccount(dbHelper.getWritableDatabase(), oldAccount.name, newName);
// update account_name of local contacts
try {
LocalAddressBook.onRenameAccount(getContext().getContentResolver(), oldAccount.name, newName);
} catch(RemoteException e) {
App.log.log(Level.SEVERE, "Couldn't propagate new account name to contacts provider");
}
// calendar provider doesn't allow changing account_name of Events
// update account_name of local tasks
try {
LocalTaskList.onRenameAccount(getContext().getContentResolver(), oldAccount.name, newName);
} catch(RemoteException e) {
App.log.log(Level.SEVERE, "Couldn't propagate new account name to tasks provider");
}
// synchronize again
requestSync(new Account(newName, oldAccount.type));
}
}, null
);
getActivity().finish();
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
}
}
/* USER ACTIONS */
private void deleteAccount() {
......@@ -576,7 +665,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
}, null);
}
private void requestSync() {
protected static void requestSync(Account account) {
String authorities[] = {
ContactsContract.AUTHORITY,
CalendarContract.AUTHORITY,
......@@ -589,7 +678,10 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); // run immediately (don't queue)
ContentResolver.requestSync(account, authority, extras);
}
}
private void requestSync() {
requestSync(account);
Snackbar.make(findViewById(R.id.parent), R.string.account_synchronizing_now, Snackbar.LENGTH_LONG).show();
}
......
......@@ -20,6 +20,10 @@
android:title="@string/account_settings"
app:showAsAction="ifRoom"/>
<item android:id="@+id/rename_account"
android:title="@string/account_rename"
app:showAsAction="never"/>
<item android:id="@+id/delete_account"
android:title="@string/account_delete"
app:showAsAction="never"/>
......
......@@ -79,6 +79,9 @@
<string name="account_synchronize_now">Jetzt synchronisieren</string>
<string name="account_synchronizing_now">Synchronisation gestartet</string>
<string name="account_settings">Konto-Einstellungen</string>
<string name="account_rename">Konto umbenennen</string>
<string name="account_rename_new_name">Ungespeicherte lokale Änderungen können verloren gehen. Nach dem Umbenennen muss neu synchronisiert werden. Neuer Kontoname:</string>
<string name="account_rename_rename">Umbenennen</string>
<string name="account_delete">Konto löschen</string>
<string name="account_delete_confirmation_title">Konto wirklich löschen?</string>
<string name="account_delete_confirmation_text">Alle Adressbücher, Kalender und Aufgabenlisten werden vom Gerät (nicht am Server) gelöscht.</string>
......
......@@ -96,6 +96,9 @@
<string name="account_synchronize_now">Synchronize now</string>
<string name="account_synchronizing_now">Synchronizing now</string>
<string name="account_settings">Account settings</string>
<string name="account_rename">Rename account</string>
<string name="account_rename_new_name">Unsaved local data may be dismissed. Re-synchronization is required after renaming. New account name:</string>
<string name="account_rename_rename">Rename</string>
<string name="account_delete">Delete account</string>
<string name="account_delete_confirmation_title">Really delete account?</string>
<string name="account_delete_confirmation_text">All local copies of address books, calendars and task lists will be deleted.</string>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment