1
0
mirror of https://github.com/etesync/android synced 2025-05-18 23:08:49 +00:00
etesync-android/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java

475 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package com.etesync.syncadapter.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.ServiceConnection;
import android.content.SyncStatusObserver;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.etesync.syncadapter.AccountSettings;
import com.etesync.syncadapter.AccountUpdateService;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.Constants;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Crypto;
import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.ui.setup.SetupUserInfoFragment;
import com.etesync.syncadapter.utils.HintManager;
import com.etesync.syncadapter.utils.ShowcaseBuilder;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.cert4android.CustomCertManager;
import at.bitfire.ical4android.TaskProvider;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
import tourguide.tourguide.ToolTip;
import static android.content.ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener, LoaderManager.LoaderCallbacks<AccountActivity.AccountInfo>, Refreshable {
public static final String EXTRA_ACCOUNT = "account";
private static final String HINT_VIEW_COLLECTION = "ViewCollection";
private Account account;
private AccountInfo accountInfo;
ListView listCalDAV, listCardDAV;
Toolbar tbCardDAV, tbCalDAV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
account = getIntent().getParcelableExtra(EXTRA_ACCOUNT);
setTitle(account.name);
setContentView(R.layout.activity_account);
Drawable icMenu = ContextCompat.getDrawable(this, R.drawable.ic_menu_light);
// CardDAV toolbar
tbCardDAV = (Toolbar)findViewById(R.id.carddav_menu);
tbCardDAV.setTitle(R.string.settings_carddav);
// CalDAV toolbar
tbCalDAV = (Toolbar)findViewById(R.id.caldav_menu);
tbCalDAV.setOverflowIcon(icMenu);
tbCalDAV.inflateMenu(R.menu.caldav_actions);
tbCalDAV.setOnMenuItemClickListener(this);
tbCalDAV.setTitle(R.string.settings_caldav);
// load CardDAV/CalDAV journals
getLoaderManager().initLoader(0, getIntent().getExtras(), this);
if (!HintManager.getHintSeen(this, HINT_VIEW_COLLECTION)) {
ShowcaseBuilder.getBuilder(this)
.setToolTip(new ToolTip().setTitle(getString(R.string.tourguide_title)).setDescription(getString(R.string.account_showcase_view_collection)))
.playOn(tbCardDAV);
HintManager.setHintSeen(this, HINT_VIEW_COLLECTION, true);
}
if (!SetupUserInfoFragment.hasUserInfo(this, account)) {
SetupUserInfoFragment.newInstance(account).show(getSupportFragmentManager(), null);
}
}
@Override
protected void onPause() {
super.onPause();
CustomCertManager certManager = ((App)getApplicationContext()).getCertManager();
if (certManager != null)
certManager.appInForeground = false;
}
@Override
protected void onResume() {
super.onResume();
CustomCertManager certManager = ((App)getApplicationContext()).getCertManager();
if (certManager != null)
certManager.appInForeground = true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_account, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sync_now:
requestSync();
break;
case R.id.settings:
Intent intent = new Intent(this, AccountSettingsActivity.class);
intent.putExtra(Constants.KEY_ACCOUNT, account);
startActivity(intent);
break;
case R.id.delete_account:
new AlertDialog.Builder(AccountActivity.this)
.setIcon(R.drawable.ic_error_dark)
.setTitle(R.string.account_delete_confirmation_title)
.setMessage(R.string.account_delete_confirmation_text)
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteAccount();
}
})
.show();
break;
case R.id.show_fingerprint:
AlertDialog dialog = new AlertDialog.Builder(AccountActivity.this)
.setIcon(R.drawable.ic_fingerprint_dark)
.setTitle(R.string.show_fingperprint_title)
.setMessage(getFormattedFingerprint())
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).create();
dialog.show();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.create_calendar:
CollectionInfo info = new CollectionInfo();
info.type = CollectionInfo.Type.CALENDAR;
startActivity(CreateCollectionActivity.newIntent(AccountActivity.this, account, info));
break;
}
return false;
}
private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final ListView list = (ListView)parent;
final ArrayAdapter<JournalEntity> adapter = (ArrayAdapter)list.getAdapter();
final JournalEntity journalEntity = adapter.getItem(position);
final CollectionInfo info = journalEntity.getInfo();
startActivity(ViewCollectionActivity.newIntent(AccountActivity.this, account, info));
}
};
private String getFormattedFingerprint() {
AccountSettings settings = null;
try {
settings = new AccountSettings(this, account);
return Crypto.AsymmetricCryptoManager.getPrettyKeyFingerprint(settings.getKeyPair().getPublicKey());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/* LOADERS AND LOADED DATA */
protected static class AccountInfo {
ServiceInfo carddav, caldav;
public static class ServiceInfo {
long id;
boolean refreshing;
List<JournalEntity> journals;
}
}
@Override
public Loader<AccountInfo> onCreateLoader(int id, Bundle args) {
return new AccountLoader(this, account);
}
@Override
public void refresh() {
getLoaderManager().restartLoader(0, getIntent().getExtras(), this);
}
@Override
public void onLoadFinished(Loader<AccountInfo> loader, final AccountInfo info) {
accountInfo = info;
CardView card = (CardView)findViewById(R.id.carddav);
if (info.carddav != null) {
ProgressBar progress = (ProgressBar)findViewById(R.id.carddav_refreshing);
progress.setVisibility(info.carddav.refreshing ? View.VISIBLE : View.GONE);
listCardDAV = (ListView)findViewById(R.id.address_books);
listCardDAV.setEnabled(!info.carddav.refreshing);
listCardDAV.setAlpha(info.carddav.refreshing ? 0.5f : 1);
final CollectionListAdapter adapter = new CollectionListAdapter(this, account);
adapter.addAll(info.carddav.journals);
listCardDAV.setAdapter(adapter);
listCardDAV.setOnItemClickListener(onItemClickListener);
} else
card.setVisibility(View.GONE);
card = (CardView)findViewById(R.id.caldav);
if (info.caldav != null) {
ProgressBar progress = (ProgressBar)findViewById(R.id.caldav_refreshing);
progress.setVisibility(info.caldav.refreshing ? View.VISIBLE : View.GONE);
listCalDAV = (ListView)findViewById(R.id.calendars);
listCalDAV.setEnabled(!info.caldav.refreshing);
listCalDAV.setAlpha(info.caldav.refreshing ? 0.5f : 1);
final CollectionListAdapter adapter = new CollectionListAdapter(this, account);
adapter.addAll(info.caldav.journals);
listCalDAV.setAdapter(adapter);
listCalDAV.setOnItemClickListener(onItemClickListener);
} else
card.setVisibility(View.GONE);
}
@Override
public void onLoaderReset(Loader<AccountInfo> loader) {
if (listCardDAV != null)
listCardDAV.setAdapter(null);
if (listCalDAV != null)
listCalDAV.setAdapter(null);
}
private static class AccountLoader extends AsyncTaskLoader<AccountInfo> implements AccountUpdateService.RefreshingStatusListener, ServiceConnection, SyncStatusObserver {
private final Account account;
private AccountUpdateService.InfoBinder davService;
private Object syncStatusListener;
public AccountLoader(Context context, Account account) {
super(context);
this.account = account;
}
@Override
protected void onStartLoading() {
syncStatusListener = ContentResolver.addStatusChangeListener(SYNC_OBSERVER_TYPE_ACTIVE, this);
getContext().bindService(new Intent(getContext(), AccountUpdateService.class), this, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStopLoading() {
davService.removeRefreshingStatusListener(this);
getContext().unbindService(this);
if (syncStatusListener != null)
ContentResolver.removeStatusChangeListener(syncStatusListener);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
davService = (AccountUpdateService.InfoBinder)service;
davService.addRefreshingStatusListener(this, false);
forceLoad();
}
@Override
public void onServiceDisconnected(ComponentName name) {
davService = null;
}
@Override
public void onDavRefreshStatusChanged(long id, boolean refreshing) {
forceLoad();
}
@Override
public void onStatusChanged(int which) {
forceLoad();
}
@Override
public AccountInfo loadInBackground() {
AccountInfo info = new AccountInfo();
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
for (ServiceEntity serviceEntity : data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account.name)).get()) {
long id = serviceEntity.getId();
CollectionInfo.Type service = serviceEntity.getType();
if (service.equals(CollectionInfo.Type.ADDRESS_BOOK)) {
info.carddav = new AccountInfo.ServiceInfo();
info.carddav.id = id;
info.carddav.refreshing = (davService != null && davService.isRefreshing(id)) || ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY);
info.carddav.journals = JournalEntity.getJournals(data, serviceEntity);
} else if (service.equals(CollectionInfo.Type.CALENDAR)) {
info.caldav = new AccountInfo.ServiceInfo();
info.caldav.id = id;
info.caldav.refreshing = (davService != null && davService.isRefreshing(id)) ||
ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) ||
ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority);
info.caldav.journals = JournalEntity.getJournals(data, serviceEntity);
}
}
return info;
}
}
/* LIST ADAPTERS */
public static class CollectionListAdapter extends ArrayAdapter<JournalEntity> {
private Account account;
public CollectionListAdapter(Context context, Account account) {
super(context, R.layout.account_collection_item);
this.account = account;
}
@Override
public View getView(int position, View v, ViewGroup parent) {
if (v == null)
v = LayoutInflater.from(getContext()).inflate(R.layout.account_collection_item, parent, false);
final JournalEntity journalEntity = getItem(position);
final CollectionInfo info = journalEntity.getInfo();
TextView tv = (TextView)v.findViewById(R.id.title);
tv.setText(TextUtils.isEmpty(info.displayName) ? info.uid : info.displayName);
tv = (TextView)v.findViewById(R.id.description);
if (TextUtils.isEmpty(info.description))
tv.setVisibility(View.GONE);
else {
tv.setVisibility(View.VISIBLE);
tv.setText(info.description);
}
final View vColor = v.findViewById(R.id.color);
if (info.type.equals(CollectionInfo.Type.ADDRESS_BOOK)) {
vColor.setVisibility(View.GONE);
} else {
if (info.color != null) {
vColor.setBackgroundColor(info.color);
} else {
vColor.setBackgroundColor(LocalCalendar.defaultColor);
}
}
View readOnly = v.findViewById(R.id.read_only);
readOnly.setVisibility(info.readOnly ? View.VISIBLE : View.GONE);
final View shared = v.findViewById(R.id.shared);
shared.setVisibility(account.name.equals(journalEntity.getOwner()) ? View.GONE : View.VISIBLE);
return v;
}
}
/* USER ACTIONS */
private void deleteAccount() {
AccountManager accountManager = AccountManager.get(this);
if (Build.VERSION.SDK_INT >= 22)
accountManager.removeAccount(account, this, new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
if (future.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT))
finish();
} catch(OperationCanceledException|IOException|AuthenticatorException e) {
App.log.log(Level.SEVERE, "Couldn't remove account", e);
}
}
}, null);
else
accountManager.removeAccount(account, new AccountManagerCallback<Boolean>() {
@Override
public void run(AccountManagerFuture<Boolean> future) {
try {
if (future.getResult())
finish();
} catch (OperationCanceledException|IOException|AuthenticatorException e) {
App.log.log(Level.SEVERE, "Couldn't remove account", e);
}
}
}, null);
}
protected static void requestSync(Account account) {
String authorities[] = {
ContactsContract.AUTHORITY,
CalendarContract.AUTHORITY,
TaskProvider.ProviderName.OpenTasks.authority
};
for (String authority : authorities) {
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); // manual sync
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();
}
}