diff --git a/MobileOrg/src/main/AndroidManifest.xml b/MobileOrg/src/main/AndroidManifest.xml index 2c306100..d4ae0c46 100644 --- a/MobileOrg/src/main/AndroidManifest.xml +++ b/MobileOrg/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ android:resource="@xml/widget_mobileorg" /> - + @@ -61,6 +61,7 @@ android:theme="@style/ActionModeAppTheme"> + @@ -70,10 +71,10 @@ + android:theme="@style/AppTheme.NoActionBar" /> + android:theme="@style/ActionModeAppTheme" /> + android:theme="@style/AppTheme.NoActionBar" /> + android:theme="@style/AppTheme.NoActionBar" /> + android:label="WebDAV Settings" /> + android:label="SDCard Settings" /> + android:label="Scp Settings" /> + android:label="Ubuntu One Settings" /> + android:theme="@style/ActionModeAppTheme" /> - + - - + + - + - - + + + android:theme="@style/AppTheme.NoActionBar" /> + - - + \ No newline at end of file diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/ConflictResolverActivity.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/ConflictResolverActivity.java new file mode 100644 index 00000000..3f78ed14 --- /dev/null +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/ConflictResolverActivity.java @@ -0,0 +1,96 @@ +package com.matburt.mobileorg2.Gui.Outline; + +import android.content.Intent; +import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.EditText; + +import com.matburt.mobileorg2.OrgData.OrgContract; +import com.matburt.mobileorg2.OrgData.OrgFile; +import com.matburt.mobileorg2.OrgData.OrgProviderUtils; +import com.matburt.mobileorg2.OrgNodeListActivity; +import com.matburt.mobileorg2.R; +import com.matburt.mobileorg2.Synchronizers.JGitWrapper; +import com.matburt.mobileorg2.Synchronizers.SynchronizerManager; +import com.matburt.mobileorg2.util.OrgFileNotFoundException; +import com.matburt.mobileorg2.util.OrgUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class ConflictResolverActivity extends AppCompatActivity { + + EditText editText; + String filename; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_conflict_resolver); + Toolbar toolbar = (Toolbar) findViewById(R.id.detail_toolbar); + setSupportActionBar(toolbar); + + // Show the Up button in the action bar. + ActionBar actionBar = getSupportActionBar(); + + + + if (savedInstanceState == null) { + // Create the detail fragment and add it to the activity + // using a fragment transaction. + Bundle arguments = new Bundle(); + Long nodeId = getIntent().getLongExtra(OrgContract.NODE_ID, -1); + + editText = (EditText)findViewById(R.id.conflict_resolver_text); + try { + OrgFile file = new OrgFile(nodeId, getContentResolver()); + if (actionBar != null) { + actionBar.setTitle(file.name); + } + + String dir = SynchronizerManager.getInstance(null,null,null).getSyncher().getAbsoluteFilesDir(this); + this.filename = dir+"/"+file.filename; + editText.setText(OrgUtils.readAll(this.filename)); + + } catch (OrgFileNotFoundException e) { + e.printStackTrace(); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.edit_node_menu, menu); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.edit_menu_cancel: + NavUtils.navigateUpTo(this, new Intent(this, OrgNodeListActivity.class)); + return true; + case R.id.edit_menu_ok: + if(this.filename!=null && !this.filename.equals("")){ + OrgUtils.writeToFile(this.filename, editText.getText().toString()); + new JGitWrapper.MergeTask(this).execute(); + } + NavUtils.navigateUpTo(this, new Intent(this, OrgNodeListActivity.class)); + return true; + } + return false; + } + + +} diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/OutlineAdapter.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/OutlineAdapter.java index e1e14762..0d95e6c7 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/OutlineAdapter.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/Gui/Outline/OutlineAdapter.java @@ -35,13 +35,13 @@ public class OutlineAdapter extends RecyclerView.Adapter { private final AppCompatActivity activity; - public List items = new ArrayList<>(); + public List items = new ArrayList<>(); ActionMode actionMode; private ContentResolver resolver; private boolean mTwoPanes = false; private SparseBooleanArray selectedItems; // Number of added items. Here it is two: Agenda and Todos. - private int numExtraItems = 2; + final private int numExtraItems = 2; private ActionMode.Callback mDeleteMode = new ActionMode.Callback() { @Override public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { @@ -110,11 +110,10 @@ public OutlineAdapter(AppCompatActivity activity) { public void refresh() { clear(); - for (OrgNode node : OrgProviderUtils.getOrgNodeChildren(-1, resolver)){ - add(node); + for (OrgFile file : OrgProviderUtils.getFiles(resolver)){ + add(file); } - notifyDataSetChanged(); } @@ -130,7 +129,11 @@ public void refresh() { @Override public void onBindViewHolder(final OutlineItem holder, final int position) { int positionInItems = position - numExtraItems; - + OrgFile file = null; + try{ + file = items.get(positionInItems); + } catch(ArrayIndexOutOfBoundsException ignored){} + final boolean conflict = (file != null && file.getState() == OrgFile.State.kConflict); String title; if(position == 0) { title = activity.getResources().getString(R.string.menu_todos); @@ -142,6 +145,15 @@ public void onBindViewHolder(final OutlineItem holder, final int position) { holder.titleView.setText(title); + TextView comment = (TextView)holder.mView.findViewById(R.id.comment); + + if (conflict) { + comment.setText(R.string.conflict); + comment.setVisibility(View.VISIBLE); + } else { + comment.setVisibility(View.GONE); + } + holder.mView.setActivated(selectedItems.get(positionInItems, false)); final long itemId = getItemId(position); @@ -165,7 +177,15 @@ public void onClick(View v) { // TODO: add agenda fragment } else { Context context = v.getContext(); - Intent intent = new Intent(context, OrgNodeDetailActivity.class); + Intent intent; + + // Special activity for conflicted file + if(conflict){ + intent = new Intent(context, ConflictResolverActivity.class); + } else { + intent = new Intent(context, OrgNodeDetailActivity.class); + } + if(position == 0){ intent.putExtra(OrgContract.NODE_ID, OrgContract.TODO_ID); @@ -208,14 +228,14 @@ public void clear() { this.items.clear(); } - public void add(OrgNode node) { - this.items.add(node); + public void add(OrgFile file) { + this.items.add(file); } @Override public long getItemId(int position) { if(position < numExtraItems) return -1; - OrgNode node = items.get(position - numExtraItems); + OrgFile node = items.get(position - numExtraItems); return node.id; } @@ -237,7 +257,6 @@ public void toggleSelection(int pos) { if(countAfter > 0 && actionMode != null){ actionMode.invalidate(); } - } public void clearSelections() { @@ -262,16 +281,8 @@ private void deleteSelectedFiles(){ List selectedItems = getSelectedItems(); for(Integer num: selectedItems){ num -= numExtraItems; - OrgNode node = items.get(num); - try { - OrgFile file = new OrgFile(node.fileId, resolver); - file.removeFile(activity); - } catch (OrgFileNotFoundException e) { - e.printStackTrace(); - } - - node.deleteNode(activity); - Log.v("selection","deleting : "+items.get(num).name); + OrgFile file = items.get(num); + file.removeFile(activity); } refresh(); actionMode.finish(); diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgContract.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgContract.java index 12330d2b..c3c539da 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgContract.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgContract.java @@ -32,7 +32,7 @@ interface FilesColumns { String ID = "_id"; String NAME = "name"; String FILENAME = "filename"; - String CHECKSUM = "checksum"; + String COMMENT = "comment"; String NODE_ID = "node_id"; } @@ -128,7 +128,7 @@ public static class Files implements FilesColumns { BASE_CONTENT_URI.buildUpon().appendPath(PATH_FILES).build(); public static final String[] DEFAULT_COLUMNS = { ID, NAME, FILENAME, - CHECKSUM, NODE_ID }; + COMMENT, NODE_ID }; public static final String DEFAULT_SORT = NAME + " ASC"; public static String getId(Uri uri) { diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgDatabase.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgDatabase.java index cf332351..87569c7a 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgDatabase.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgDatabase.java @@ -5,12 +5,9 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteStatement; -import android.util.Log; import com.matburt.mobileorg2.OrgData.OrgContract.OrgData; -import java.lang.reflect.Field; -import java.util.ArrayList; import java.util.HashMap; public class OrgDatabase extends SQLiteOpenHelper { @@ -24,74 +21,22 @@ public class OrgDatabase extends SQLiteOpenHelper { private int orgdata_priorityColumn; private int orgdata_parentidColumn; private int orgdata_fileidColumn; - private int orgdata_levelColumn; - private int orgdata_positionColumn; + private int orgdata_levelColumn; + private int orgdata_positionColumn; private InsertHelper orgdataInsertHelper; private SQLiteStatement addPayloadStatement; private SQLiteStatement addTimestampsStatement; - - - public interface PrioritiesTable{ - String tableName = "priorities"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] name = {"name", "text"}; - } - - public interface FilesTable{ - String tableName = "files"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] name = {"name", "text"}; - String[] node_id = {"node_id", "integer"}; - String[] filename = {"filename", "text"}; - String[] comment = {"comment", "text)"}; - } - - - public interface TodoTable{ - String tableName = "todos"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] name = {"name", "text"}; - String[] todogroup = {"todogroup", "integer"}; - String[] isdone = {"isdone", "integer", "default 0"}; - String[] todoConstrain = {"UNIQUE(todogroup, name) ON CONFLICT IGNORE"}; - } - - public interface TagsTable{ - String tableName = "files"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] name = {"name", "text"}; - String[] taggroup = {"taggroup", "integer"}; + public interface Tables { + String EDITS = "edits"; + String FILES = "files"; + String PRIORITIES = "priorities"; + String TAGS = "tags"; + String TODOS = "todos"; + String ORGDATA = "orgdata"; } - public interface EditsTable{ - String tableName = "edits"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] type = {"type", "text"}; - String[] title = {"title", "text"}; - String[] data_id = {"data_id", "integer"}; - String[] old_value = {"old_value", "text"}; - String[] new_value = {"new_value", "text"}; - String[] changed = {"changed", "integer)"}; - } - - public interface OrgDataTable{ - String tableName = "orgdata"; - String[] id = {"_id", "integer primary key autoincrement"}; - String[] name = {"name", "text"}; - String[] parent_id = {"parent_id", "integer", "default -1"}; - String[] file_id = {"file_id", "integer"}; - String[] level = {"level", "integer", "default 0"}; - String[] priority = {"priority", "text"}; - String[] todo = {"todo", "text"}; - String[] tags = {"tags", "text"}; - String[] tags_inherited = {"tags_inherited", "text"}; - String[] payload = {"payload", "text"}; - String[] position = {"position", "integer"}; - String[] scheduled = {"scheduled", "integer", "default -1"}; - String[] deadline = {"deadline", "integer", "default -1"}; - } private static OrgDatabase mInstance = null; public static OrgDatabase getInstance(Context context){ @@ -105,42 +50,78 @@ private OrgDatabase(Context context) { @Override public void onCreate(SQLiteDatabase db) { - Log.v("swag","megaswag : " + new Table().createDB()); - + db.execSQL("CREATE TABLE IF NOT EXISTS files(" + + "_id integer primary key autoincrement," + + "node_id integer," + + "filename text," + + "name text," + + "comment text)"); + db.execSQL("CREATE TABLE IF NOT EXISTS todos(" + + "_id integer primary key autoincrement," + + "todogroup integer," + + "name text," + + "isdone integer default 0," + + "UNIQUE(todogroup, name) ON CONFLICT IGNORE)"); + db.execSQL("CREATE TABLE IF NOT EXISTS priorities(" + + "_id integer primary key autoincrement," + + "name text)"); + db.execSQL("CREATE TABLE IF NOT EXISTS tags(" + + "_id integer primary key autoincrement," + + "taggroup integer," + + "name text)"); + db.execSQL("CREATE TABLE IF NOT EXISTS edits(" + + "_id integer primary key autoincrement," + + "type text," + + "title text," + + "data_id integer," + + "old_value text," + + "new_value text," + + "changed integer)"); + db.execSQL("CREATE TABLE IF NOT EXISTS orgdata (" + + "_id integer primary key autoincrement," + + "parent_id integer default -1," + + "file_id integer," + + "level integer default 0," + + "priority text," + + "todo text," + + "tags text," + + "tags_inherited text," + + "payload text," + + "name text," + + "position integer," + + "scheduled integer default -1," + + "deadline integer default -1)"); } - - - @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (newVersion) { - case 4: - db.execSQL("DROP TABLE IF EXISTS priorities"); - db.execSQL("DROP TABLE IF EXISTS files"); - db.execSQL("DROP TABLE IF EXISTS todos"); - db.execSQL("DROP TABLE IF EXISTS edits"); - db.execSQL("DROP TABLE IF EXISTS orgdata"); - break; - - case 5: - db.execSQL("alter table orgdata add tags_inherited text"); - break; + case 4: + db.execSQL("DROP TABLE IF EXISTS priorities"); + db.execSQL("DROP TABLE IF EXISTS files"); + db.execSQL("DROP TABLE IF EXISTS todos"); + db.execSQL("DROP TABLE IF EXISTS edits"); + db.execSQL("DROP TABLE IF EXISTS orgdata"); + break; + + case 5: + db.execSQL("alter table orgdata add tags_inherited text"); + break; } onCreate(db); } public long fastInsertNode(OrgNode node) { prepareOrgdataInsert(); - orgdataInsertHelper.bind(orgdata_parentidColumn, node.parentId); + orgdataInsertHelper.bind(orgdata_parentidColumn, node.parentId); orgdataInsertHelper.bind(orgdata_nameColumn, node.name); orgdataInsertHelper.bind(orgdata_todoColumn, node.todo); orgdataInsertHelper.bind(orgdata_priorityColumn, node.priority); orgdataInsertHelper.bind(orgdata_fileidColumn, node.fileId); orgdataInsertHelper.bind(orgdata_tagsColumn, node.tags); orgdataInsertHelper.bind(orgdata_tagsInheritedColumn, node.tags_inherited); - orgdataInsertHelper.bind(orgdata_levelColumn, node.level); - orgdataInsertHelper.bind(orgdata_positionColumn, node.position); + orgdataInsertHelper.bind(orgdata_levelColumn, node.level); + orgdataInsertHelper.bind(orgdata_positionColumn, node.position); return orgdataInsertHelper.execute(); } @@ -157,21 +138,20 @@ public void fastInsertNodePayload(Long id, final String payload, final HashMap res = OrgNodeTree.getFullNodeArray(tree, true); - for (OrgNode node : res) { - Log.v("content","content"); - Log.v("content",node.toString()); - content += FileUtils.stripLastNewLine(node.toString()) + "\n"; - } - - file.updateFile(content, context); + file.updateFile(file.toString(resolver), context); } catch (OrgFileNotFoundException e) { e.printStackTrace(); - } catch (OrgNodeNotFoundException e) { - e.printStackTrace(); } diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgFile.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgFile.java index 07ee7dfc..9a199730 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgFile.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgFile.java @@ -11,12 +11,14 @@ import com.matburt.mobileorg2.OrgData.OrgContract.OrgData; import com.matburt.mobileorg2.Synchronizers.Synchronizer; import com.matburt.mobileorg2.Synchronizers.SynchronizerManager; +import com.matburt.mobileorg2.util.FileUtils; import com.matburt.mobileorg2.util.OrgFileNotFoundException; import com.matburt.mobileorg2.util.OrgNodeNotFoundException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.util.ArrayList; public class OrgFile { public static final String CAPTURE_FILE = "mobileorg.org"; @@ -26,11 +28,17 @@ public class OrgFile { public String filename = ""; public String name = ""; + public String comment = ""; public boolean includeInOutline = true; public long id = -1; public long nodeId = -1; + public enum State { + kOK, + kConflict + } + public OrgFile() { } @@ -71,20 +79,26 @@ public OrgFile(String filename, ContentResolver resolver) throws OrgFileNotFound cursor.close(); } + public void set(Cursor cursor) throws OrgFileNotFoundException { if (cursor != null && cursor.getCount() > 0) { if (cursor.isBeforeFirst() || cursor.isAfterLast()) cursor.moveToFirst(); - this.name = cursor.getString(cursor.getColumnIndexOrThrow(Files.NAME)); - this.filename = cursor.getString(cursor.getColumnIndexOrThrow(Files.FILENAME)); + this.name = OrgProviderUtils.getNonNullString(cursor, + cursor.getColumnIndexOrThrow(Files.NAME)); + this.filename = OrgProviderUtils.getNonNullString(cursor, + cursor.getColumnIndexOrThrow(Files.FILENAME)); this.id = cursor.getLong(cursor.getColumnIndexOrThrow(Files.ID)); this.nodeId = cursor.getLong(cursor.getColumnIndexOrThrow(Files.NODE_ID)); + this.comment = OrgProviderUtils.getNonNullString(cursor, + cursor.getColumnIndexOrThrow(Files.COMMENT)); } else { throw new OrgFileNotFoundException( "Failed to create OrgFile from cursor"); } } + public boolean doesFileExist(ContentResolver resolver) { Cursor cursor = resolver.query(Files.buildFilenameUri(filename), Files.DEFAULT_COLUMNS, null, null, null); @@ -207,7 +221,7 @@ private long removeFileNode(ContentResolver resolver) { * @param values * @return */ - private long updateFileNode(ContentResolver resolver, ContentValues values) { + public long updateFileInDB(ContentResolver resolver, ContentValues values) { return resolver.update(Files.buildIdUri(id), values, Files.NAME + "=? AND " + Files.FILENAME + "=?", new String[]{name, filename}); } @@ -237,4 +251,32 @@ public String getFilePath(Context context) { Synchronizer synchronizer = SynchronizerManager.getInstance(null, null, null).getSyncher(); return synchronizer.getAbsoluteFilesDir(context) + "/" + filename; } + + /** + * Query the state of the file (conflicted or not) + * @return + */ + public State getState(){ + return comment.equals("conflict") ? State.kConflict : State.kOK; + } + + + public String toString(ContentResolver resolver) { + String result = ""; + OrgNode root = null; + try { + root = new OrgNode(nodeId, resolver); + OrgNodeTree tree = new OrgNodeTree(root, resolver); + ArrayList res = OrgNodeTree.getFullNodeArray(tree, true); + for (OrgNode node : res) { + Log.v("content", "content"); + Log.v("content", node.toString()); + result += FileUtils.stripLastNewLine(node.toString()) + "\n"; + } + } catch (OrgNodeNotFoundException e) { + e.printStackTrace(); + } + return result; + } } + diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProvider.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProvider.java index b5c32098..67989879 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProvider.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProvider.java @@ -13,6 +13,7 @@ import com.matburt.mobileorg2.OrgData.OrgContract.Files; import com.matburt.mobileorg2.OrgData.OrgContract.OrgData; import com.matburt.mobileorg2.OrgData.OrgContract.Search; +import com.matburt.mobileorg2.OrgData.OrgDatabase.Tables; import com.matburt.mobileorg2.util.SelectionBuilder; public class OrgProvider extends ContentProvider { @@ -130,67 +131,66 @@ public String getType(Uri uri) { private SelectionBuilder buildSelectionFromUri(Uri uri) { final SelectionBuilder builder = new SelectionBuilder(); -// switch (uriMatcher.match(uri)) { -// case ORGDATA: -// return builder.table(Tables.ORGDATA); -// case ORGDATA_ID: -// return builder.table(Tables.ORGDATA).where(OrgData.ID + "=?", OrgData.getId(uri)); -// case ORGDATA_PARENT: -// return builder.table(Tables.ORGDATA).where(OrgData.ID + "=?", OrgData.getId(uri)); -// case ORGDATA_CHILDREN: -// return builder.table(Tables.ORGDATA).where(OrgData.PARENT_ID + "=?", OrgData.getId(uri)); -// case FILES: -// return builder.table(Tables.FILES); -// case FILES_ID: -// return builder.table(Tables.FILES).where(Files.ID + "=?", Files.getId(uri)); -// case FILES_FILENAME: -// return builder.table(Tables.FILES).where(Files.FILENAME + "=?", Files.getFilename(uri)); -// case EDITS: -// return builder.table(Tables.EDITS); -// case EDITS_ID: -// return builder.table(Tables.EDITS).where(Edits.ID + "=?", Edits.getId(uri)); -// case TAGS: -// return builder.table(Tables.TAGS); -// case TODOS: -// return builder.table(Tables.TODOS); -// case PRIORITIES: -// return builder.table(Tables.PRIORITIES); -// case SEARCH: -// final String search = Search.getSearchTerm(uri); -// return builder.table(Tables.ORGDATA).where("name LIKE %?%", search); -// default: -// throw new IllegalArgumentException("Unknown URI " + uri); -// } - return null; + switch (uriMatcher.match(uri)) { + case ORGDATA: + return builder.table(Tables.ORGDATA); + case ORGDATA_ID: + return builder.table(Tables.ORGDATA).where(OrgData.ID + "=?", OrgData.getId(uri)); + case ORGDATA_PARENT: + return builder.table(Tables.ORGDATA).where(OrgData.ID + "=?", OrgData.getId(uri)); + case ORGDATA_CHILDREN: + return builder.table(Tables.ORGDATA).where(OrgData.PARENT_ID + "=?", OrgData.getId(uri)); + case FILES: + return builder.table(Tables.FILES); + case FILES_ID: + return builder.table(Tables.FILES).where(Files.ID + "=?", Files.getId(uri)); + case FILES_FILENAME: + return builder.table(Tables.FILES).where(Files.FILENAME + "=?", Files.getFilename(uri)); + case EDITS: + return builder.table(Tables.EDITS); + case EDITS_ID: + return builder.table(Tables.EDITS).where(Edits.ID + "=?", Edits.getId(uri)); + case TAGS: + return builder.table(Tables.TAGS); + case TODOS: + return builder.table(Tables.TODOS); + case PRIORITIES: + return builder.table(Tables.PRIORITIES); + case SEARCH: + final String search = Search.getSearchTerm(uri); + return builder.table(Tables.ORGDATA).where("name LIKE %?%", search); + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } } private String getTableNameFromUri(Uri uri) { String tableName = null; -// switch(uriMatcher.match(uri)) { -// case ORGDATA: -// tableName = Tables.ORGDATA; -// break; -// case FILES: -// tableName = Tables.FILES; -// break; -// case EDITS: -// tableName = Tables.EDITS; -// break; -// case TAGS: -// tableName = Tables.TAGS; -// break; -// case TODOS: -// tableName = Tables.TODOS; -// break; -// case PRIORITIES: -// tableName = Tables.PRIORITIES; -// break; -// -// default: -// throw new IllegalArgumentException("Unknown URI " + uri); -// } - + switch(uriMatcher.match(uri)) { + case ORGDATA: + tableName = Tables.ORGDATA; + break; + case FILES: + tableName = Tables.FILES; + break; + case EDITS: + tableName = Tables.EDITS; + break; + case TAGS: + tableName = Tables.TAGS; + break; + case TODOS: + tableName = Tables.TODOS; + break; + case PRIORITIES: + tableName = Tables.PRIORITIES; + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + return tableName; } } diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProviderUtils.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProviderUtils.java index 6146cabf..ec6281d5 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProviderUtils.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/OrgData/OrgProviderUtils.java @@ -33,6 +33,7 @@ public static List getFileNodes(Context context){ return OrgProviderUtils.getOrgNodeChildren(-1, context.getContentResolver()); } + public static ArrayList getFilenames(ContentResolver resolver) { ArrayList result = new ArrayList(); @@ -55,6 +56,39 @@ public static ArrayList getFilenames(ContentResolver resolver) { } + /** + * Query the DB for the list of files + * @param resolver + * @return + */ + public static ArrayList getFiles(ContentResolver resolver) { + ArrayList result = new ArrayList<>(); + + Cursor cursor = resolver.query(Files.CONTENT_URI, Files.DEFAULT_COLUMNS, + null, null, Files.DEFAULT_SORT); + if(cursor == null) return result; + cursor.moveToFirst(); + + while (!cursor.isAfterLast()) { + OrgFile orgFile = new OrgFile(); + + try { + orgFile.set(cursor); + result.add(orgFile); + } catch (OrgFileNotFoundException e) {} + cursor.moveToNext(); + } + + cursor.close(); + return result; + } + + public static String getNonNullString(Cursor cursor, int index){ + String result = cursor.getString(index); + return result!=null ? result : ""; + } + + public static void addTodos(HashMap todos, ContentResolver resolver) { if(todos == null) return; diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/Synchronizers/JGitWrapper.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/Synchronizers/JGitWrapper.java index cb4eaf31..0d0648a8 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/Synchronizers/JGitWrapper.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/Synchronizers/JGitWrapper.java @@ -1,31 +1,23 @@ package com.matburt.mobileorg2.Synchronizers; import android.app.Activity; -import android.app.Application; -import android.app.Dialog; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AlertDialog; import android.util.Log; -import android.widget.Button; import android.widget.Toast; -import com.matburt.mobileorg2.OrgData.OrgContract; import com.matburt.mobileorg2.OrgData.OrgDatabase; import com.matburt.mobileorg2.OrgData.OrgFile; import com.matburt.mobileorg2.OrgData.OrgFileParser; import com.matburt.mobileorg2.OrgData.OrgProviderUtils; import com.matburt.mobileorg2.R; import com.matburt.mobileorg2.util.FileUtils; +import com.matburt.mobileorg2.util.OrgFileNotFoundException; -import org.eclipse.jgit.api.CheckoutResult; +import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.errors.CanceledException; @@ -45,7 +37,9 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import java.io.BufferedReader; @@ -147,6 +141,7 @@ public static SyncResult pull(final Context context) { result.setState(SyncResult.State.kSuccess); return result; } catch(WrongRepositoryStateException e){ + e.printStackTrace(); handleMergeConflict(git, context); } catch (IOException | DetachedHeadException @@ -297,6 +292,69 @@ protected Void doInBackground(String... params) { e.printStackTrace(); } catch (WrongRepositoryStateException e) { e.printStackTrace(); + handleMergeConflict(git,context); + } catch (ConcurrentRefUpdateException e) { + e.printStackTrace(); + } catch (NoHeadException e) { + e.printStackTrace(); + } catch (NoMessageException e) { + e.printStackTrace(); + } catch (GitAPIException e) { + e.printStackTrace(); + } + return null; + } + } + + static public class MergeTask extends AsyncTask { + Context context; + + public MergeTask(Context context) { + this.context = context; + } + + protected Void doInBackground(String... params) { + Log.v("git", "merging"); + + File repoDir = new File(context.getFilesDir() + "/" + GIT_DIR + "/.git"); + Git git = null; + try { + git = Git.open(repoDir); + + CheckoutCommand coCmd = git.checkout(); +// Commands are part of the api module, which include git-like calls + coCmd.setName("master"); + coCmd.setCreateBranch(false); // probably not needed, just to make sure + Ref ref = coCmd.call(); + + // Stage all changed files, omitting new files, and commit with one command + git.merge() + .setStrategy(MergeStrategy.OURS) + .include(ref) + .call(); + + git.add() + .addFilepattern("google.org").call(); + + org.eclipse.jgit.api.Status status = git.status().call(); + System.out.println("Added: " + status.getAdded()); + System.out.println("Changed: " + status.getChanged()); + System.out.println("Conflicting: " + status.getConflicting()); + System.out.println("Missing: " + status.getMissing()); + System.out.println("Modified: " + status.getModified()); + System.out.println("Removed: " + status.getRemoved()); + System.out.println("Untracked: " + status.getUntracked()); + + AuthData authData = AuthData.getInstance(context); + git.push() + .setCredentialsProvider(new CredentialsProviderAllowHost(authData.getUser(), authData.getPassword())) + .call(); + System.out.println("Committed all changes to repository at "); + } catch (IOException | UnmergedPathsException e) { + e.printStackTrace(); + } catch (WrongRepositoryStateException e) { + e.printStackTrace(); + handleMergeConflict(git,context); } catch (ConcurrentRefUpdateException e) { e.printStackTrace(); } catch (NoHeadException e) { @@ -321,17 +379,17 @@ private static void handleMergeConflict(Git git, Context context){ status = git.status().call(); ContentResolver resolver = context.getContentResolver(); for(String file: status.getConflicting()){ -// OrgFile f = new OrgFile(f, resolver); -// ContentValues values = new ContentValues(); -// values.put() -// f.updateFile(resolver, values); - - + OrgFile f = new OrgFile(file, resolver); + ContentValues values = new ContentValues(); + values.put("comment","conflict"); + f.updateFileInDB(resolver, values); } } catch (GitAPIException e1) { e1.printStackTrace(); return; + } catch (OrgFileNotFoundException e) { + e.printStackTrace(); } diff --git a/MobileOrg/src/main/java/com/matburt/mobileorg2/util/OrgUtils.java b/MobileOrg/src/main/java/com/matburt/mobileorg2/util/OrgUtils.java index 445da107..90702bce 100644 --- a/MobileOrg/src/main/java/com/matburt/mobileorg2/util/OrgUtils.java +++ b/MobileOrg/src/main/java/com/matburt/mobileorg2/util/OrgUtils.java @@ -20,36 +20,42 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; public class OrgUtils { - + public static String getTimestamp() { - SimpleDateFormat sdf = new SimpleDateFormat("[yyyy-MM-dd EEE HH:mm]"); + SimpleDateFormat sdf = new SimpleDateFormat("[yyyy-MM-dd EEE HH:mm]"); return sdf.format(new Date()); } - - public static void setupSpinnerWithEmpty(Spinner spinner, ArrayList data, - String selection) { + + public static void setupSpinnerWithEmpty(Spinner spinner, ArrayList data, + String selection) { data.add(""); setupSpinner(spinner, data, selection); - } - + } + public static void setupSpinner(Spinner spinner, ArrayList data, - String selection) { + String selection) { if(!TextUtils.isEmpty(selection) && !data.contains(selection)) data.add(selection); - + ArrayAdapter adapter = new ArrayAdapter(spinner.getContext(), android.R.layout.simple_spinner_item, data); adapter.setDropDownViewResource(R.layout.edit_spinner_layout); @@ -60,7 +66,7 @@ public static void setupSpinner(Spinner spinner, ArrayList data, } spinner.setSelection(pos, true); } - + public static OrgNode getCaptureIntentContents(Intent intent) { String subject = intent .getStringExtra("android.intent.extra.SUBJECT"); @@ -70,7 +76,7 @@ public static OrgNode getCaptureIntentContents(Intent intent) { subject = "[[" + text + "][" + subject + "]]"; text = ""; } - + if(subject == null) subject = ""; if(text == null) @@ -81,59 +87,59 @@ public static OrgNode getCaptureIntentContents(Intent intent) { node.setPayload(text); return node; } - + public static long getNodeFromPath(String path, ContentResolver resolver) throws OrgFileNotFoundException { String filename = path.substring("file://".length(), path.length()); - + // TODO Handle links to headings instead of simply stripping it out if(filename.indexOf(":") > -1) filename = filename.substring(0, filename.indexOf(":")); - + OrgFile file = new OrgFile(filename, resolver); return file.nodeId; } - + public static void announceSyncDone(Context context) { Intent intent = new Intent(SynchronizerManager.SYNC_UPDATE); intent.putExtra(SynchronizerManager.SYNC_DONE, true); context.sendBroadcast(intent); } - + public static void announceSyncStart(Context context) { Intent intent = new Intent(SynchronizerManager.SYNC_UPDATE); intent.putExtra(SynchronizerManager.SYNC_START, true); context.sendBroadcast(intent); } - + public static void announceSyncUpdateProgress(int progress, Context context) { Intent intent = new Intent(SynchronizerManager.SYNC_UPDATE); intent.putExtra(SynchronizerManager.SYNC_PROGRESS_UPDATE, progress); context.sendBroadcast(intent); } - public static String getStringFromResource(int resource, Context context) { - InputStream is = context.getResources().openRawResource(resource); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String readLine = null; - String contents = ""; - - try { - // While the BufferedReader readLine is not null - while ((readLine = br.readLine()) != null) { - contents += readLine + "\n"; - } - - // Close the InputStream and BufferedReader - is.close(); - br.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - return contents; - } - + public static String getStringFromResource(int resource, Context context) { + InputStream is = context.getResources().openRawResource(resource); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String readLine = null; + String contents = ""; + + try { + // While the BufferedReader readLine is not null + while ((readLine = br.readLine()) != null) { + contents += readLine + "\n"; + } + + // Close the InputStream and BufferedReader + is.close(); + br.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + return contents; + } + public static String rightTrim(String str) { int last = str.length() - 1; int end = last; @@ -208,7 +214,7 @@ public static String lookUpValueFromArray(Context context, int keyID, int valID, } return null; } - + public static boolean isWifiOnline(Context context) { ConnectivityManager conMan = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -250,4 +256,38 @@ public int compare(Object o1, Object o2) { return s1.toLowerCase().compareTo(s2.toLowerCase()); } } + + /** + * Read content of file on disk + * @param filename + * The absolute filename + * @return + */ + static public String readAll(String filename){ + try { + FileReader fileReader = new FileReader(filename); + BufferedReader bufferedReader = new BufferedReader(fileReader); + StringBuilder stringBuilder = new StringBuilder(); + + String currentLine; + while ((currentLine = bufferedReader.readLine()) != null) stringBuilder.append(currentLine + "\n"); + return stringBuilder.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + static public void writeToFile(String filename, String content) { + try { + File file = new File(filename); + OutputStream outputStream = new FileOutputStream(file); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + outputStreamWriter.write(content); + outputStreamWriter.close(); + } + catch (IOException e) { + Log.e("Exception", "File write failed: " + e.toString()); + } + } } diff --git a/MobileOrg/src/main/res/layout/activity_conflict_resolver.xml b/MobileOrg/src/main/res/layout/activity_conflict_resolver.xml new file mode 100644 index 00000000..4cad4b62 --- /dev/null +++ b/MobileOrg/src/main/res/layout/activity_conflict_resolver.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/MobileOrg/src/main/res/layout/outline_item.xml b/MobileOrg/src/main/res/layout/outline_item.xml index 667939a0..1bb0c75f 100644 --- a/MobileOrg/src/main/res/layout/outline_item.xml +++ b/MobileOrg/src/main/res/layout/outline_item.xml @@ -20,24 +20,32 @@ card_view:cardCornerRadius="4dp" android:layout_margin="5dp"> - + - - + \ No newline at end of file diff --git a/MobileOrg/src/main/res/values-w820dp/dimens.xml b/MobileOrg/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..63fc8164 --- /dev/null +++ b/MobileOrg/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/MobileOrg/src/main/res/values/dimens.xml b/MobileOrg/src/main/res/values/dimens.xml index be6feca2..a4489c29 100644 --- a/MobileOrg/src/main/res/values/dimens.xml +++ b/MobileOrg/src/main/res/values/dimens.xml @@ -13,4 +13,7 @@ 35dip 45dip 45dip + + 16dp + 16dp diff --git a/MobileOrg/src/main/res/values/strings.xml b/MobileOrg/src/main/res/values/strings.xml index 0b9855f0..0370c98e 100644 --- a/MobileOrg/src/main/res/values/strings.xml +++ b/MobileOrg/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ Filename Details You have no node in this file + Conflict You cannot select this item