diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3c2fd429..3a3ddfdb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -181,7 +181,7 @@
android:name=".ui.AccountActivity"
android:parentActivityName=".ui.AccountsActivity">
-
+
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java
index f487d68f..362453c8 100644
--- a/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java
+++ b/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java
@@ -180,13 +180,13 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
final ArrayAdapter adapter = (ArrayAdapter)list.getAdapter();
final CollectionInfo info = adapter.getItem(position);
- startActivity(EditCollectionActivity.newIntent(AccountActivity.this, account, info, (adapter.getCount() > 1)));
+ startActivity(ViewCollectionActivity.newIntent(AccountActivity.this, account, info));
}
};
public void onChangeJournalClick(View view) {
- Intent intent = new Intent(this, JournalViewerActivity.class);
- intent.putExtra(JournalViewerActivity.EXTRA_ACCOUNT, account);
+ Intent intent = new Intent(this, ViewCollectionActivity.class);
+ intent.putExtra(ViewCollectionActivity.EXTRA_ACCOUNT, account);
startActivity(intent);
}
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java
index a0465812..ded3d778 100644
--- a/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java
+++ b/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java
@@ -18,20 +18,20 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
+import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo;
+import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.resource.LocalCalendar;
+import io.requery.Persistable;
+import io.requery.sql.EntityDataStore;
+
public class EditCollectionActivity extends CreateCollectionActivity {
- private final static String EXTRA_ALLOW_DELETE = "allowDelete";
-
- protected boolean allowDelete;
-
- public static Intent newIntent(Context context, Account account, CollectionInfo info, boolean allowDelete) {
+ public static Intent newIntent(Context context, Account account, CollectionInfo info) {
Intent intent = new Intent(context, EditCollectionActivity.class);
intent.putExtra(CreateCollectionActivity.EXTRA_ACCOUNT, account);
intent.putExtra(CreateCollectionActivity.EXTRA_COLLECTION_INFO, info);
- intent.putExtra(EXTRA_ALLOW_DELETE, allowDelete);
return intent;
}
@@ -39,8 +39,6 @@ public class EditCollectionActivity extends CreateCollectionActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- allowDelete = getIntent().getExtras().getBoolean(EXTRA_ALLOW_DELETE, false);
-
setTitle(R.string.edit_collection);
if (info.type == CollectionInfo.Type.CALENDAR) {
@@ -70,7 +68,10 @@ public class EditCollectionActivity extends CreateCollectionActivity {
}
public void onDeleteCollection(MenuItem item) {
- if (!allowDelete) {
+ EntityDataStore data = ((App) getApplication()).getData();
+ int journalCount = data.count(JournalEntity.class).where(JournalEntity.SERVICE.eq(info.serviceID)).get().value();
+
+ if (journalCount < 2) {
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_error_dark)
.setTitle(R.string.account_delete_collection_last_title)
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/JournalViewerActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/JournalViewerActivity.java
deleted file mode 100644
index 74215855..00000000
--- a/app/src/main/java/com/etesync/syncadapter/ui/JournalViewerActivity.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.NavUtils;
-import android.support.v7.app.AppCompatActivity;
-import android.view.MenuItem;
-
-import com.etesync.syncadapter.App;
-import com.etesync.syncadapter.ui.journalviewer.ListJournalsFragment;
-
-public class JournalViewerActivity extends AppCompatActivity {
- public final static String EXTRA_ACCOUNT = "account";
-
- private Account account;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- if (savedInstanceState == null) {
- account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
- // first call, add fragment
- getSupportFragmentManager().beginTransaction()
- .replace(android.R.id.content, ListJournalsFragment.newInstance(account))
- .commit();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- if (!getSupportFragmentManager().popBackStackImmediate()) {
- finish();
- }
- return true;
- }
- return false;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- App app = (App)getApplicationContext();
- if (app.getCertManager() != null)
- app.getCertManager().appInForeground = true;
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- App app = (App)getApplicationContext();
- if (app.getCertManager() != null)
- app.getCertManager().appInForeground = false;
- }
-}
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java
new file mode 100644
index 00000000..cdaec8ba
--- /dev/null
+++ b/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.etesync.syncadapter.App;
+import com.etesync.syncadapter.R;
+import com.etesync.syncadapter.model.CollectionInfo;
+import com.etesync.syncadapter.model.EntryEntity;
+import com.etesync.syncadapter.model.JournalEntity;
+import com.etesync.syncadapter.resource.LocalAddressBook;
+import com.etesync.syncadapter.resource.LocalCalendar;
+import com.etesync.syncadapter.ui.journalviewer.ListEntriesFragment;
+
+import java.util.Locale;
+
+import at.bitfire.ical4android.CalendarStorageException;
+import at.bitfire.vcard4android.ContactsStorageException;
+import io.requery.Persistable;
+import io.requery.sql.EntityDataStore;
+
+public class ViewCollectionActivity extends AppCompatActivity {
+ public final static String EXTRA_ACCOUNT = "account",
+ EXTRA_COLLECTION_INFO = "collectionInfo";
+
+ private Account account;
+ protected CollectionInfo info;
+
+ public static Intent newIntent(Context context, Account account, CollectionInfo info) {
+ Intent intent = new Intent(context, ViewCollectionActivity.class);
+ intent.putExtra(ViewCollectionActivity.EXTRA_ACCOUNT, account);
+ intent.putExtra(ViewCollectionActivity.EXTRA_COLLECTION_INFO, info);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.view_collection_activity);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
+ info = (CollectionInfo) getIntent().getExtras().getSerializable(EXTRA_COLLECTION_INFO);
+
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.list_entries_container, ListEntriesFragment.newInstance(info))
+ .commit();
+
+
+ final TextView stats = (TextView) findViewById(R.id.stats);
+
+ final View colorSquare = findViewById(R.id.color);
+ if (info.type == CollectionInfo.Type.CALENDAR) {
+ if (info.color != null) {
+ colorSquare.setBackgroundColor(info.color);
+ } else {
+ colorSquare.setBackgroundColor(LocalCalendar.defaultColor);
+ }
+
+ try {
+ LocalCalendar resource = (LocalCalendar) LocalCalendar.find(account, this.getContentResolver().acquireContentProviderClient(CalendarContract.CONTENT_URI),
+ LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.NAME + "=?", new String[]{info.url})[0];
+ long count = resource.count();
+ EntityDataStore data = ((App) getApplication()).getData();
+ int entryCount = -1;
+ final JournalEntity journalEntity = data.select(JournalEntity.class).where(JournalEntity.UID.eq(info.url)).limit(1).get().firstOrNull();
+ if (journalEntity != null) {
+ entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value();
+ }
+ stats.setText(String.format(Locale.getDefault(), "Events: %d, Journal entries: %d", count, entryCount));
+ } catch (CalendarStorageException e) {
+ e.printStackTrace();
+ stats.setText("Stats loading error.");
+ }
+ } else {
+ colorSquare.setVisibility(View.GONE);
+
+ try {
+ LocalAddressBook resource = new LocalAddressBook(account, this.getContentResolver().acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI));
+ long count = resource.count();
+ EntityDataStore data = ((App) getApplication()).getData();
+ int entryCount = -1;
+ final JournalEntity journalEntity = data.select(JournalEntity.class).where(JournalEntity.UID.eq(info.url)).limit(1).get().firstOrNull();
+ if (journalEntity != null) {
+ entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value();
+ };
+ stats.setText(String.format(Locale.getDefault(), "Contacts: %d, Journal Entries: %d", count, entryCount));
+ } catch (ContactsStorageException e) {
+ e.printStackTrace();
+ stats.setText("Stats loading error.");
+ }
+ }
+
+ final TextView title = (TextView) findViewById(R.id.display_name);
+ title.setText(info.displayName);
+
+ final TextView desc = (TextView) findViewById(R.id.description);
+ desc.setText(info.description);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_view_collection, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ if (!getSupportFragmentManager().popBackStackImmediate()) {
+ finish();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ App app = (App) getApplicationContext();
+ if (app.getCertManager() != null)
+ app.getCertManager().appInForeground = true;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ App app = (App) getApplicationContext();
+ if (app.getCertManager() != null)
+ app.getCertManager().appInForeground = false;
+ }
+
+ public void onEditCollection(MenuItem item) {
+ startActivity(EditCollectionActivity.newIntent(this, account, info));
+ // FIXME: Handle it more gracefully
+ finish();
+ }
+}
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java
index 3bcef1e1..21b53bbe 100644
--- a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java
+++ b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java
@@ -10,6 +10,7 @@ package com.etesync.syncadapter.ui.journalviewer;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
@@ -21,6 +22,7 @@ import android.widget.TextView;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
+import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
@@ -31,15 +33,15 @@ import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
public class ListEntriesFragment extends ListFragment implements AdapterView.OnItemClickListener {
- protected static final String EXTRA_JOURNAL = "journal";
+ protected static final String EXTRA_COLLECTION_INFO = "collectionInfo";
private EntityDataStore data;
private JournalEntity journalEntity;
- public static ListEntriesFragment newInstance(String journal) {
+ public static ListEntriesFragment newInstance(CollectionInfo info) {
ListEntriesFragment frag = new ListEntriesFragment();
Bundle args = new Bundle(1);
- args.putSerializable(EXTRA_JOURNAL, journal);
+ args.putSerializable(EXTRA_COLLECTION_INFO, info);
frag.setArguments(args);
return frag;
}
@@ -48,14 +50,14 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
data = ((App) getContext().getApplicationContext()).getData();
- String name = getArguments().getString(EXTRA_JOURNAL);
- journalEntity = JournalModel.Journal.fetch(data, name);
+ CollectionInfo info = (CollectionInfo) getArguments().getSerializable(EXTRA_COLLECTION_INFO);
+ journalEntity = JournalModel.Journal.fetch(data, info.url);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- getActivity().setTitle(String.format(Locale.getDefault(), "%s (%d)", journalEntity.getInfo().displayName, data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value()));
- return inflater.inflate(R.layout.journal_viewer_list_entries, container, false);
+ getActivity().setTitle(journalEntity.getInfo().displayName);
+ return inflater.inflate(R.layout.journal_viewer_list, container, false);
}
@Override
@@ -74,24 +76,52 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
public void onItemClick(AdapterView> parent, View view, int position, long id) {
EntryEntity entry = (EntryEntity) getListAdapter().getItem(position);
new AlertDialog.Builder(getActivity())
- .setTitle("Raw dump: " + entry.getUid())
- .setMessage("Action: " + entry.getContent().getAction().toString() + "\nUid: " + entry.getUid() + "\n" + entry.getContent().getContent()).show();
+ .setTitle("Raw dump")
+ .setMessage("Action: " + entry.getContent().getAction().toString() + "\nIntegrity: " + entry.getUid() + "\n" + entry.getContent().getContent()).show();
}
- static class EntriesListAdapter extends ArrayAdapter {
- public EntriesListAdapter(Context context) {
- super(context, R.layout.journal_viewer_list_journals_item);
+ class EntriesListAdapter extends ArrayAdapter {
+ EntriesListAdapter(Context context) {
+ super(context, R.layout.journal_viewer_list_item);
}
+ private String getLine(String content, String prefix) {
+ int start = content.indexOf(prefix);
+ if (start >= 0) {
+ int end = content.indexOf("\n", start);
+ content = content.substring(start + prefix.length(), end);
+ } else {
+ content = null;
+ }
+ return content;
+ }
@Override
- public View getView(int position, View v, ViewGroup parent) {
+ @NonNull
+ public View getView(int position, View v, @NonNull ViewGroup parent) {
if (v == null)
- v = LayoutInflater.from(getContext()).inflate(R.layout.journal_viewer_list_journals_item, parent, false);
+ v = LayoutInflater.from(getContext()).inflate(R.layout.journal_viewer_list_item, parent, false);
EntryEntity entryEntity = getItem(position);
TextView tv = (TextView) v.findViewById(R.id.title);
- tv.setText(String.format(Locale.getDefault(), "%s: %s", entryEntity.getContent().getAction().toString(), entryEntity.getUid()));
+
+ // FIXME: hacky way to make it show sensible info
+ CollectionInfo info = journalEntity.getInfo();
+ String fullContent = entryEntity.getContent().getContent();
+ String prefix;
+ if (info.type == CollectionInfo.Type.CALENDAR) {
+ prefix = "SUMMARY:";
+ } else {
+ prefix = "FN:";
+ }
+ String content = getLine(fullContent, prefix);
+ content = (content != null) ? content : entryEntity.getUid().substring(0, 20);
+ tv.setText(String.format(Locale.getDefault(), "%s: %s", entryEntity.getContent().getAction().toString(), content));
+
+ tv = (TextView) v.findViewById(R.id.description);
+ content = getLine(fullContent, "UID:");
+ content = "UID: " + ((content != null) ? content : "Not found");
+ tv.setText(content);
return v;
}
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListJournalsFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListJournalsFragment.java
index dd189915..601abfe5 100644
--- a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListJournalsFragment.java
+++ b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListJournalsFragment.java
@@ -87,7 +87,7 @@ public class ListJournalsFragment extends ListFragment implements AdapterView.On
if (item.getViewType() == 1) {
CollectionInfo info = ((ListItem) item).info;
getFragmentManager().beginTransaction()
- .replace(android.R.id.content, ListEntriesFragment.newInstance(info.url))
+ .replace(android.R.id.content, ListEntriesFragment.newInstance(info))
.addToBackStack(null)
.commitAllowingStateLoss();
}
diff --git a/app/src/main/res/layout/journal_viewer_list_entries.xml b/app/src/main/res/layout/journal_viewer_list.xml
similarity index 90%
rename from app/src/main/res/layout/journal_viewer_list_entries.xml
rename to app/src/main/res/layout/journal_viewer_list.xml
index 1d8e24b2..e21e5e44 100644
--- a/app/src/main/res/layout/journal_viewer_list_entries.xml
+++ b/app/src/main/res/layout/journal_viewer_list.xml
@@ -7,9 +7,7 @@
-
+ android:layout_height="match_parent" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_collection_activity.xml b/app/src/main/res/layout/view_collection_activity.xml
new file mode 100644
index 00000000..34106bfe
--- /dev/null
+++ b/app/src/main/res/layout/view_collection_activity.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/activity_view_collection.xml b/app/src/main/res/menu/activity_view_collection.xml
new file mode 100644
index 00000000..530e8426
--- /dev/null
+++ b/app/src/main/res/menu/activity_view_collection.xml
@@ -0,0 +1,17 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fde1276b..26e26a86 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -187,6 +187,7 @@
Display name (title) of this collection:
Title is required
Description (optional):
+ Edit
Save
Delete
Are you sure?