1
0
mirror of https://github.com/etesync/android synced 2024-11-16 04:49:06 +00:00

JournalViewer: add an activity to view the journal.

It's very raw and hacky at the moment, it's just a preview release so
people could see their data is saved, and can look at it in its raw
form until we implement a nicer view.
This commit is contained in:
Tom Hacohen 2017-03-09 17:46:06 +00:00
parent 42a644cabb
commit 3530821ddd
12 changed files with 478 additions and 11 deletions

View File

@ -181,6 +181,7 @@
android:name=".ui.AccountActivity"
android:parentActivityName=".ui.AccountsActivity">
</activity>
<activity android:name=".ui.JournalViewerActivity"/>
<activity android:name=".ui.AccountSettingsActivity"/>
<activity android:name=".ui.CreateAddressBookActivity"
android:label="@string/create_addressbook"/>

View File

@ -182,6 +182,12 @@ public class ServiceDB {
else
return null;
}
@Nullable
public Long getService(@NonNull Account account, String service) {
@Cleanup SQLiteDatabase db = getReadableDatabase();
return getService(db, account, service);
}
}

View File

@ -50,12 +50,6 @@ import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.cert4android.CustomCertManager;
import com.etesync.syncadapter.AccountUpdateService;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.Constants;
@ -65,6 +59,13 @@ import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB.OpenHelper;
import com.etesync.syncadapter.model.ServiceDB.Services;
import com.etesync.syncadapter.resource.LocalCalendar;
import java.io.IOException;
import java.util.LinkedList;
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;
@ -205,6 +206,11 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
}
};
public void onChangeJournalClick(View view) {
Intent intent = new Intent(this, JournalViewerActivity.class);
intent.putExtra(JournalViewerActivity.EXTRA_ACCOUNT, account);
startActivity(intent);
}
/* LOADERS AND LOADED DATA */

View File

@ -0,0 +1,69 @@
/*
* 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;
}
}

View File

@ -0,0 +1,99 @@
/*
* 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.journalviewer;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.etesync.syncadapter.App;
import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import java.util.Locale;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
public class ListEntriesFragment extends ListFragment implements AdapterView.OnItemClickListener {
protected static final String EXTRA_JOURNAL = "journal";
private EntityDataStore<Persistable> data;
private JournalEntity journalEntity;
public static ListEntriesFragment newInstance(String journal) {
ListEntriesFragment frag = new ListEntriesFragment();
Bundle args = new Bundle(1);
args.putSerializable(EXTRA_JOURNAL, journal);
frag.setArguments(args);
return frag;
}
@Override
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);
}
@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);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EntriesListAdapter listAdapter = new EntriesListAdapter(getContext());
setListAdapter(listAdapter);
listAdapter.addAll(data.select(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).orderBy(EntryEntity.ID.desc()).get().toList());
getListView().setOnItemClickListener(this);
}
@Override
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();
}
static class EntriesListAdapter extends ArrayAdapter<EntryEntity> {
public EntriesListAdapter(Context context) {
super(context, R.layout.journal_viewer_list_journals_item);
}
@Override
public View getView(int position, View v, ViewGroup parent) {
if (v == null)
v = LayoutInflater.from(getContext()).inflate(R.layout.journal_viewer_list_journals_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()));
return v;
}
}
}

View File

@ -0,0 +1,196 @@
/*
* 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.journalviewer;
import android.accounts.Account;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
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.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
import lombok.Cleanup;
public class ListJournalsFragment extends ListFragment implements AdapterView.OnItemClickListener {
private final static String ARG_ACCOUNT = "account";
private Account account;
public static ListJournalsFragment newInstance(Account account) {
ListJournalsFragment frag = new ListJournalsFragment();
Bundle args = new Bundle(1);
args.putParcelable(ARG_ACCOUNT, account);
frag.setArguments(args);
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
account = getArguments().getParcelable(ARG_ACCOUNT);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getActivity().setTitle(R.string.change_journal_title);
return inflater.inflate(R.layout.journal_viewer_list_journals, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CollectionsListAdapter listAdapter = new CollectionsListAdapter(getContext());
setListAdapter(listAdapter);
final EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
Long service = dbHelper.getService(account, ServiceDB.Services.SERVICE_CARDDAV);
listAdapter.add(new HeaderItem(getString(R.string.settings_carddav)));
for (CollectionInfo info : JournalEntity.getCollections(data, service)) {
listAdapter.add(new ListItem(info));
}
service = dbHelper.getService(account, ServiceDB.Services.SERVICE_CALDAV);
listAdapter.add(new HeaderItem(getString(R.string.settings_caldav)));
for (CollectionInfo info : JournalEntity.getCollections(data, service)) {
listAdapter.add(new ListItem(info));
}
getListView().setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Item item = (Item) getListAdapter().getItem(position);
if (item.getViewType() == 1) {
CollectionInfo info = ((ListItem) item).info;
getFragmentManager().beginTransaction()
.replace(android.R.id.content, ListEntriesFragment.newInstance(info.url))
.addToBackStack(null)
.commitAllowingStateLoss();
}
}
private static class CollectionsListAdapter extends ArrayAdapter<Item> {
CollectionsListAdapter(Context context) {
super(context, 0);
}
@NonNull
@Override
public View getView(int position, View v, @NonNull ViewGroup parent) {
Item item = getItem(position);
LayoutInflater inflater = LayoutInflater.from(getContext());
if (v == null) {
v = item.getView(inflater, v, parent);
}
return v;
}
@Override
public int getViewTypeCount() {
return Item.Type.values().length;
}
@Override
public int getItemViewType(int position) {
return getItem(position).getViewType();
}
}
interface Item {
enum Type {
Header(0),
Item(1);
private final int value;
Type(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
int getViewType();
View getView(LayoutInflater inflater, View convertView, ViewGroup parent);
}
private static class HeaderItem implements Item {
private String header;
HeaderItem(String header) {
this.header = header;
}
@Override
public int getViewType() {
return Type.Header.getValue();
}
@Override
public View getView(LayoutInflater inflater, View v, ViewGroup parent) {
if (v == null)
v = inflater.inflate(R.layout.journal_viewer_list_journals_header, parent, false);
TextView tv = (TextView) v.findViewById(R.id.title);
tv.setTypeface(null, Typeface.BOLD);
tv.setText(header);
return v;
}
}
private static class ListItem implements Item {
private CollectionInfo info;
ListItem(CollectionInfo info) {
this.info = info;
}
@Override
public int getViewType() {
return Type.Item.getValue();
}
@Override
public View getView(LayoutInflater inflater, View v, ViewGroup parent) {
if (v == null)
v = inflater.inflate(R.layout.journal_viewer_list_journals_item, parent, false);
TextView tv = (TextView) v.findViewById(R.id.title);
tv.setText(info.displayName);
return v;
}
}
}

View File

@ -118,7 +118,7 @@
android:theme="@style/toolbar_theme"
style="@style/toolbar_style"
app:navigationIcon="@drawable/ic_journal"
app:title="Change Journal"
app:title="@string/change_journal_title"
android:elevation="2dp" tools:ignore="UnusedAttribute"/>
@ -126,20 +126,22 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical">
<LinearLayout android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:orientation="vertical">
<TextView
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Coming Soon..."/>
android:onClick="onChangeJournalClick"
android:gravity="left|center_vertical"
android:padding="16dp"
style="@style/Widget.AppCompat.Button.Borderless"
android:text="View (ugly preview-release)"/>
</LinearLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/journal_entries_list_empty"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical">
<LinearLayout android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
tools:text="Title"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical">
<LinearLayout android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="Title"/>
</LinearLayout>
</LinearLayout>

View File

@ -90,6 +90,8 @@
<string name="account_delete_collection_last_title">Can\'t delete last collection</string>
<string name="account_delete_collection_last_text">Deleting the last collection is not allowed, please create a new one if you\'d like to delete this one.</string>
<string name="change_journal_title">Change Journal</string>
<!-- PermissionsActivity -->
<string name="permissions_title">EteSync permissions</string>
<string name="permissions_calendar">Calendar permissions</string>
@ -191,6 +193,9 @@
<string name="delete_collection_deleting_collection">Deleting collection</string>
<string name="collection_stats_title">Stats</string>
<!-- JournalViewer -->
<string name="journal_entries_list_empty">No entries found for this journal</string>
<!-- ExceptionInfoFragment -->
<string name="exception">An error has occurred.</string>
<string name="exception_httpexception">An HTTP error has occurred.</string>