├── mOrgAnd ├── .gitignore ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── arrays.xml │ │ │ │ ├── strings.xml │ │ │ │ └── strings_settings.xml │ │ │ ├── drawable-hdpi │ │ │ │ ├── file.png │ │ │ │ ├── folder.png │ │ │ │ ├── ic_launcher.png │ │ │ │ └── outline_item_selected.xml │ │ │ ├── drawable-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── xml │ │ │ │ ├── pref_general.xml │ │ │ │ ├── pref_headers.xml │ │ │ │ ├── pref_interface.xml │ │ │ │ ├── pref_data_sync.xml │ │ │ │ ├── pref_calendar.xml │ │ │ │ └── pref_git.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── edit_text_fragment.xml │ │ │ │ ├── fragment_main.xml │ │ │ │ ├── file_dialog_row.xml │ │ │ │ ├── outline_item.xml │ │ │ │ ├── edit_heading_fragment.xml │ │ │ │ ├── edit_date_fragment.xml │ │ │ │ ├── file_dialog_main.xml │ │ │ │ └── fragment_outline.xml │ │ │ └── menu │ │ │ │ ├── outline.xml │ │ │ │ └── main.xml │ │ ├── java │ │ │ └── com │ │ │ │ ├── hdweiss │ │ │ │ └── morgand │ │ │ │ │ ├── events │ │ │ │ │ ├── DataUpdatedEvent.java │ │ │ │ │ └── SyncEvent.java │ │ │ │ │ ├── gui │ │ │ │ │ ├── edit │ │ │ │ │ │ ├── controller │ │ │ │ │ │ │ ├── EditController.java │ │ │ │ │ │ │ ├── BaseEditController.java │ │ │ │ │ │ │ └── AddController.java │ │ │ │ │ │ ├── EditTextFragment.java │ │ │ │ │ │ ├── EditHeadingFragment.java │ │ │ │ │ │ ├── BaseEditFragment.java │ │ │ │ │ │ └── EditDateFragment.java │ │ │ │ │ ├── theme │ │ │ │ │ │ ├── MonoTheme.java │ │ │ │ │ │ ├── WhiteTheme.java │ │ │ │ │ │ └── DefaultTheme.java │ │ │ │ │ ├── AgendaFragment.java │ │ │ │ │ ├── MainPagerAdapter.java │ │ │ │ │ ├── outline │ │ │ │ │ │ ├── OutlineActionMode.java │ │ │ │ │ │ ├── OutlineFragment.java │ │ │ │ │ │ ├── OutlineListView.java │ │ │ │ │ │ └── OutlineAdapter.java │ │ │ │ │ ├── SynchronizerNotification.java │ │ │ │ │ └── MainActivity.java │ │ │ │ │ ├── utils │ │ │ │ │ ├── MultiMap.java │ │ │ │ │ ├── FileUtils.java │ │ │ │ │ ├── Utils.java │ │ │ │ │ └── SafeAsyncTask.java │ │ │ │ │ ├── data │ │ │ │ │ ├── dao │ │ │ │ │ │ ├── OrgAgenda.java │ │ │ │ │ │ ├── OrgFile.java │ │ │ │ │ │ ├── DatabaseHelper.java │ │ │ │ │ │ └── OrgNodeRepository.java │ │ │ │ │ ├── CalendarUtils.java │ │ │ │ │ ├── OrgNodeUtils.java │ │ │ │ │ ├── OrgCalendarEntry.java │ │ │ │ │ └── OrgNodeTimeDate.java │ │ │ │ │ ├── settings │ │ │ │ │ ├── LocalPathSettingsActivity.java │ │ │ │ │ ├── PreferenceUtils.java │ │ │ │ │ ├── KeySettingActivity.java │ │ │ │ │ └── SettingsActivity.java │ │ │ │ │ ├── synchronizer │ │ │ │ │ ├── calendar │ │ │ │ │ │ ├── CalendarEntry.java │ │ │ │ │ │ ├── CalendarEntriesParser.java │ │ │ │ │ │ └── SyncCalendarTask.java │ │ │ │ │ ├── git │ │ │ │ │ │ ├── JGitConfigSessionFactory.java │ │ │ │ │ │ ├── JGitCredentialsProvider.java │ │ │ │ │ │ └── SyncGitTask.java │ │ │ │ │ ├── parser │ │ │ │ │ │ ├── SyncParserTask.java │ │ │ │ │ │ └── OrgRepository.java │ │ │ │ │ ├── writer │ │ │ │ │ │ ├── SyncWriterTask.java │ │ │ │ │ │ └── OrgFileWriter.java │ │ │ │ │ └── SyncService.java │ │ │ │ │ └── Application.java │ │ │ │ └── lamerman │ │ │ │ └── SelectionMode.java │ │ └── AndroidManifest.xml │ ├── debug │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── hdweiss │ │ └── morgand │ │ └── test │ │ ├── OrgNodeStubbed.java │ │ ├── TestUtils.java │ │ ├── JGitWrapperTests.java │ │ ├── OrgFileWriterTests.java │ │ └── OrgFileTestScenario.java ├── proguard-rules.txt └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.org ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /mOrgAnd/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'mOrgAnd' 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /mOrgAnd/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #66000000 4 | 5 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-hdpi/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-hdpi/file.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-hdpi/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-hdpi/folder.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/events/DataUpdatedEvent.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.events; 2 | 3 | public class DataUpdatedEvent { 4 | } 5 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdweiss/mOrgAnd/HEAD/mOrgAnd/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mOrgAnd/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | mOrgAnd debug 4 | 5 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/lamerman/SelectionMode.java: -------------------------------------------------------------------------------- 1 | package com.lamerman; 2 | 3 | public class SelectionMode { 4 | public static final int MODE_CREATE = 0; 5 | 6 | public static final int MODE_OPEN = 1; 7 | } 8 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_general.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 13 11:53:21 CEST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip 7 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | All documentation for this project is located at [[https://github.com/hdweiss/mOrgAnd/wiki]] 2 | 3 | * Building 4 | Follow the instructions at [[https://github.com/hdweiss/mOrgAnd/wiki/Starting%20Development][Starting Development]] 5 | 6 | * Notes 7 | - This project uses https://code.google.com/p/android-file-dialog/ 8 | 9 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 70dp 7 | 70dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/drawable-hdpi/outline_item_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/controller/EditController.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit.controller; 2 | 3 | import com.hdweiss.morgand.data.dao.OrgNode; 4 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 5 | 6 | public class EditController extends BaseEditController { 7 | 8 | public EditController(OrgNode node) { 9 | mode = EditMode.Edit; 10 | this.node = node; 11 | } 12 | 13 | @Override 14 | public void save(OrgNode node) { 15 | OrgNodeRepository.update(node); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/controller/BaseEditController.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit.controller; 2 | 3 | import com.hdweiss.morgand.data.dao.OrgNode; 4 | 5 | public abstract class BaseEditController { 6 | public enum EditMode {Add, Edit} 7 | 8 | protected EditMode mode; 9 | protected OrgNode node; 10 | 11 | public abstract void save(OrgNode node); 12 | 13 | public EditMode getMode() { 14 | return this.mode; 15 | } 16 | 17 | public OrgNode getNode() { 18 | return this.node; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/menu/outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/edit_text_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Android 2 | *.apk 3 | gen/ 4 | build/ 5 | local.properties 6 | .gradle/ 7 | # Old Android 8 | bin/ 9 | 10 | # Idea 11 | # According to this 12 | # see http://devnet.jetbrains.com/docs/DOC-1186 13 | # I shouldn't ignore most of this stuff.. but looking at the content 14 | # I see environment stuffs in it. 15 | # And I don't see any particular advantage in having the idea stuff 16 | # in my repo 17 | # it would be helpful for code templates but it's simply not worth 18 | # the effort currently 19 | *.iws 20 | *.iml 21 | *.ipr 22 | .idea 23 | out 24 | 25 | # Eclipse 26 | .project 27 | .classpath 28 | .settings 29 | 30 | # Mac 31 | .DS_Store 32 | 33 | # Generic 34 | *~ 35 | .swp 36 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/events/SyncEvent.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.events; 2 | 3 | public class SyncEvent { 4 | public enum State { 5 | Done, Intermediate, Progress, SecondaryProgress 6 | } 7 | 8 | public State state; 9 | public int progress = 0; 10 | public String filename = ""; 11 | 12 | public SyncEvent(State state) { 13 | this(state, 0); 14 | } 15 | 16 | public SyncEvent(State state, int progress) { 17 | this(state, progress, ""); 18 | } 19 | 20 | public SyncEvent(State state, int progress, String filename) { 21 | this.state = state; 22 | this.progress = progress; 23 | this.filename = filename; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mOrgAnd/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/hdw/Applications/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/controller/AddController.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit.controller; 2 | 3 | import com.hdweiss.morgand.Application; 4 | import com.hdweiss.morgand.data.dao.OrgNode; 5 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 6 | import com.hdweiss.morgand.events.DataUpdatedEvent; 7 | 8 | public class AddController extends BaseEditController { 9 | 10 | public AddController(OrgNode parent, OrgNode.Type type) { 11 | mode = EditController.EditMode.Add; 12 | this.node = parent.addChild(type); 13 | } 14 | 15 | @Override 16 | public void save(OrgNode node) { 17 | OrgNodeRepository.create(node); 18 | Application.getBus().post(new DataUpdatedEvent()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 11 | 12 | 17 | 19 | 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /mOrgAnd/src/androidTest/java/com/hdweiss/morgand/test/OrgNodeStubbed.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.test; 2 | 3 | import com.hdweiss.morgand.data.dao.OrgNode; 4 | 5 | public class OrgNodeStubbed extends OrgNode { 6 | 7 | public int nextNodeLineNumber; 8 | public int siblingLineNumber; 9 | 10 | public OrgNodeStubbed(int lineNumber) { 11 | this.lineNumber = lineNumber; 12 | } 13 | 14 | public OrgNodeStubbed(int lineNumber, int nextNodeLineNumber, int siblingLineNumber) { 15 | super(); 16 | this.lineNumber = lineNumber; 17 | this.nextNodeLineNumber = nextNodeLineNumber; 18 | this.siblingLineNumber = siblingLineNumber; 19 | } 20 | 21 | @Override 22 | public int getNextNodeLineNumber() { 23 | return nextNodeLineNumber; 24 | } 25 | 26 | @Override 27 | public int getSiblingLineNumber() { 28 | return siblingLineNumber; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/file_dialog_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 100000 5 | 300000 6 | 600000 7 | 900000 8 | 1800000 9 | 2700000 10 | 3600000 11 | 10800000 12 | 21600000 13 | 43200000 14 | 15 | 16 | 17 | theirs 18 | ours 19 | 20 | 21 | 22 | Git 23 | Update files 24 | 25 | 26 | 27 | git 28 | none 29 | 30 | 31 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_headers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
6 | 7 |
10 | 11 |
15 | 16 |
20 | 21 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_interface.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/theme/MonoTheme.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.theme; 2 | 3 | import android.graphics.Color; 4 | 5 | public class MonoTheme extends DefaultTheme { 6 | 7 | public MonoTheme() { 8 | super(); 9 | c0Black = Color.rgb(0xff, 0xff, 0xff); 10 | c1Red = Color.rgb(0xd0, 0x00, 0x00); 11 | c2Green = Color.rgb(0x00, 0x00, 0x00); 12 | c3Yellow = Color.rgb(0x00, 0x00, 0x00); 13 | c4Blue = Color.rgb(0x00, 0x00, 0x00); 14 | c5Purple = Color.rgb(0x00, 0x00, 0x00); 15 | c6Cyan = Color.rgb(0x00, 0x00, 0x00); 16 | c7White = Color.rgb(0x00, 0x00, 0x00); 17 | 18 | c9LRed = Color.rgb(0x00, 0x00, 0x00); 19 | caLGreen = Color.rgb(0x77, 0xff, 0x77); 20 | cbLYellow = Color.rgb(0x00, 0x00, 0x00); 21 | ccLBlue = Color.rgb(0x00, 0x00, 0x00); 22 | cdLPurple = Color.rgb(0x00, 0x00, 0x00); 23 | ceLCyan = Color.rgb(0x00, 0x00, 0x00); 24 | cfLWhite = Color.rgb(0x00, 0x00, 0x00); 25 | 26 | levelColors = new int[] { cfLWhite }; 27 | 28 | defaultFontColor = "black"; 29 | defaultBackground = Color.rgb(0xff, 0xff, 0xff); 30 | defaultForeground = Color.rgb(0x10, 0x10, 0x10); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/theme/WhiteTheme.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.theme; 2 | 3 | import android.graphics.Color; 4 | 5 | public class WhiteTheme extends DefaultTheme { 6 | 7 | public WhiteTheme() { 8 | super(); 9 | c0Black = Color.rgb(0xff, 0xff, 0xff); 10 | c1Red = Color.rgb(0x20, 0x00, 0x00); 11 | c2Green = Color.rgb(0x00, 0x20, 0x00); 12 | c3Yellow = Color.rgb(0x20, 0x20, 0x00); 13 | c4Blue = Color.rgb(0x0, 0x0, 0x20); 14 | c5Purple = Color.rgb(0x20, 0x00, 0x20); 15 | c6Cyan = Color.rgb(0x00, 0x20, 0x20); 16 | c7White = Color.rgb(0x10, 0x10, 0x10); 17 | 18 | c9LRed = Color.rgb(0xa0, 0x0, 0x0); 19 | caLGreen = Color.rgb(0x20, 0xa0, 0x20); 20 | cbLYellow = Color.rgb(0x80, 0x80, 0x00); 21 | ccLBlue = Color.rgb(0x00, 0x00, 0x80); 22 | cdLPurple = Color.rgb(0x80, 0x00, 0x80); 23 | ceLCyan = Color.rgb(0x00, 0x80, 0x80); 24 | cfLWhite = Color.rgb(0x00, 0x00, 0x00); 25 | 26 | levelColors = new int[] { ccLBlue, c3Yellow, ceLCyan, c2Green, 27 | c5Purple, ccLBlue, c2Green, ccLBlue, c3Yellow, ceLCyan }; 28 | 29 | defaultFontColor = "black"; 30 | defaultBackground = Color.rgb(0xff, 0xff, 0xff); 31 | defaultForeground = Color.rgb(0x10, 0x10, 0x10); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/utils/MultiMap.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Set; 6 | 7 | public class MultiMap { 8 | 9 | private HashMap> entryMap = new HashMap>(); 10 | 11 | public void put(Long key, T value) { 12 | ArrayList valueList = entryMap.get(key); 13 | 14 | if(valueList == null) { 15 | valueList = new ArrayList(); 16 | entryMap.put(key, valueList); 17 | } 18 | 19 | valueList.add(value); 20 | } 21 | 22 | public ArrayList get(Long key) { 23 | return entryMap.get(key); 24 | } 25 | 26 | public void remove(long key, T value) { 27 | ArrayList valueList = entryMap.get(key); 28 | 29 | if(valueList != null) { 30 | valueList.remove(value); 31 | } 32 | } 33 | 34 | public Set keySet() { 35 | return entryMap.keySet(); 36 | } 37 | 38 | public T findValue(long key, Object object) throws IllegalArgumentException { 39 | ArrayList matches = entryMap.get(key); 40 | 41 | if (matches == null) 42 | return null; 43 | 44 | for (T match : matches) { 45 | if (match.equals(object)) 46 | return match; 47 | } 48 | 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /mOrgAnd/src/androidTest/java/com/hdweiss/morgand/test/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.test; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.BufferedWriter; 5 | import java.io.File; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileReader; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | 11 | public class TestUtils { 12 | 13 | public static void writeStringAsFile(final String fileContents, String fileName) { 14 | try { 15 | BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); 16 | writer.write(fileContents); 17 | writer.close(); 18 | } catch (IOException e) { 19 | } 20 | } 21 | 22 | public static String readFileAsString(String fileName) { 23 | StringBuilder stringBuilder = new StringBuilder(); 24 | String line; 25 | BufferedReader in = null; 26 | 27 | try { 28 | in = new BufferedReader(new FileReader(new File(fileName))); 29 | while ((line = in.readLine()) != null) stringBuilder.append(line).append("\n"); 30 | 31 | } catch (FileNotFoundException e) { 32 | } catch (IOException e) { 33 | } 34 | 35 | return stringBuilder.toString(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/dao/OrgAgenda.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data.dao; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | import java.sql.SQLException; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @DatabaseTable(tableName = "OrgAgendas") 11 | public class OrgAgenda { 12 | 13 | @DatabaseField(generatedId = true) 14 | public int Id; 15 | 16 | public static final String TITLE_FIELD_NAME = "title"; 17 | @DatabaseField(columnName = TITLE_FIELD_NAME) 18 | public String title; 19 | 20 | @DatabaseField 21 | public String files; 22 | 23 | @DatabaseField 24 | public String tags; 25 | 26 | @DatabaseField 27 | public String priorities; 28 | 29 | @DatabaseField 30 | public String todos; 31 | 32 | @DatabaseField 33 | public boolean includeHabits; 34 | 35 | @DatabaseField 36 | public boolean includeInactiveTodos; 37 | 38 | 39 | public List getNodes() { 40 | try { 41 | return OrgNodeRepository.queryBuilder().query(); 42 | } catch (SQLException e) { 43 | e.printStackTrace(); 44 | return new ArrayList(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | 9 | public class FileUtils { 10 | 11 | public static boolean deleteDirectory(File path) { 12 | if( path.exists() ) { 13 | File[] files = path.listFiles(); 14 | if (files == null) { 15 | return true; 16 | } 17 | for(int i=0; i fileToArrayList(String fileName) throws IOException { 31 | String line; 32 | BufferedReader in = new BufferedReader(new FileReader(new File(fileName))); 33 | ArrayList fileContent = new ArrayList(); 34 | while ((line = in.readLine()) != null) 35 | fileContent.add(line); 36 | 37 | return fileContent; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/outline_item.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /mOrgAnd/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android' 2 | 3 | def getVersionName = { -> 4 | def stdout = new ByteArrayOutputStream() 5 | exec { 6 | commandLine 'git', 'describe', '--tags' 7 | standardOutput = stdout 8 | } 9 | return stdout.toString().trim() 10 | } 11 | 12 | android { 13 | compileSdkVersion 20 14 | buildToolsVersion '20' 15 | 16 | defaultConfig { 17 | minSdkVersion 14 18 | targetSdkVersion 16 19 | versionCode 1 20 | versionName getVersionName() + "-alpha" 21 | } 22 | buildTypes { 23 | debug { 24 | applicationIdSuffix ".debug" 25 | versionNameSuffix "debug" 26 | } 27 | release { 28 | runProguard false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 30 | // applicationVariants.all { variant -> 31 | // def file = variant.outputFile 32 | // variant.outputFile = new File(file.parent, file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk")) 33 | // } 34 | } 35 | } 36 | } 37 | 38 | dependencies { 39 | compile 'com.android.support:support-v4:20.0.0' 40 | compile 'com.j256.ormlite:ormlite-android:4.48' 41 | compile 'org.eclipse.jgit:org.eclipse.jgit:3.4.1.201406201815-r' 42 | compile 'com.squareup:otto:1.3.5' 43 | compile fileTree(dir: 'libs', include: ['*.jar']) 44 | } 45 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/settings/LocalPathSettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.settings; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.preference.PreferenceManager; 8 | 9 | import com.lamerman.FileDialog; 10 | import com.lamerman.SelectionMode; 11 | 12 | public class LocalPathSettingsActivity extends Activity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | 18 | Intent intent = new Intent(getBaseContext(), FileDialog.class); 19 | intent.putExtra(FileDialog.START_PATH, "/sdcard"); 20 | intent.putExtra(FileDialog.CAN_SELECT_DIR, true); 21 | intent.putExtra(FileDialog.SELECTION_MODE, SelectionMode.MODE_CREATE); 22 | 23 | startActivityForResult(intent, 0); 24 | } 25 | 26 | @Override 27 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 28 | if (resultCode == Activity.RESULT_OK) { 29 | String filePath = data.getStringExtra(FileDialog.RESULT_PATH); 30 | 31 | SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(this).edit(); 32 | edit.putString("git_local_path", filePath); 33 | edit.commit(); 34 | 35 | } else if (resultCode == Activity.RESULT_CANCELED) { 36 | } 37 | finish(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/EditTextFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.EditText; 8 | 9 | import com.hdweiss.morgand.R; 10 | import com.hdweiss.morgand.data.dao.OrgNode; 11 | 12 | public class EditTextFragment extends BaseEditFragment { 13 | 14 | private EditText editText; 15 | 16 | @Override 17 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 18 | View view = inflater.inflate(R.layout.edit_text_fragment, container, false); 19 | 20 | editText = (EditText) view.findViewById(R.id.editText); 21 | 22 | return view; 23 | } 24 | 25 | @Override 26 | public void onViewCreated(View view, Bundle savedInstanceState) { 27 | super.onViewCreated(view, savedInstanceState); 28 | 29 | if (controller != null) { 30 | String text = controller.getNode().title; 31 | populateView(text); 32 | } 33 | } 34 | 35 | private void populateView(String text) { 36 | if (text != null) 37 | editText.setText(text); 38 | 39 | getDialog().setTitle(R.string.action_edit); 40 | } 41 | 42 | @Override 43 | public OrgNode getEditedNode() { 44 | OrgNode editNode = controller.getNode(); 45 | editNode.title = editText.getText().toString(); 46 | return editNode; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/calendar/CalendarEntry.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.calendar; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.hdweiss.morgand.data.CalendarUtils; 6 | import com.hdweiss.morgand.data.OrgCalendarEntry; 7 | import com.hdweiss.morgand.data.dao.OrgNode; 8 | 9 | public class CalendarEntry { 10 | public String title = ""; 11 | public String description = ""; 12 | public String location = ""; 13 | public long id = -1; 14 | public long dtStart = 0; 15 | public long dtEnd = 0; 16 | public int allDay = 0; 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (o instanceof OrgCalendarEntry) { 21 | OrgCalendarEntry entry = (OrgCalendarEntry) o; 22 | return this.dtStart == entry.beginTime 23 | && this.dtEnd == entry.endTime 24 | && entry.getCalendarTitle().startsWith(this.title); 25 | } 26 | 27 | return super.equals(o); 28 | } 29 | 30 | public OrgNode writeToOrgNodes(OrgNode parentNode) { 31 | OrgNode headingNode = parentNode.addChild(OrgNode.Type.Headline, this.title); 32 | 33 | boolean isAllDay = allDay > 0; 34 | String date = CalendarUtils.formatDate(this.dtStart, this.dtEnd, isAllDay); 35 | 36 | headingNode.addChild(OrgNode.Type.Date, date); 37 | headingNode.addChild(OrgNode.Type.Body, this.description); 38 | 39 | if (TextUtils.isEmpty(this.location) == false) 40 | headingNode.addChild(OrgNode.Type.Drawer, ":LOCATION: " + this.location); 41 | 42 | return headingNode; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/Application.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand; 2 | 3 | import android.content.SharedPreferences; 4 | import android.preference.PreferenceManager; 5 | 6 | import com.hdweiss.morgand.events.SyncEvent; 7 | import com.hdweiss.morgand.synchronizer.SyncService; 8 | import com.squareup.otto.Bus; 9 | import com.squareup.otto.Produce; 10 | import com.squareup.otto.Subscribe; 11 | 12 | public class Application extends android.app.Application { 13 | 14 | private static Application instace; 15 | public static Application getInstace() { 16 | return instace; 17 | } 18 | 19 | private static Bus bus; 20 | public static Bus getBus() { 21 | if (bus == null) 22 | bus = new Bus(); 23 | return bus; 24 | } 25 | 26 | private SyncEvent syncEvent = new SyncEvent(SyncEvent.State.Done); 27 | 28 | @Produce public SyncEvent produceSyncEvent() { 29 | return this.syncEvent; 30 | } 31 | 32 | @Subscribe public void syncEvent(SyncEvent event) { 33 | this.syncEvent = event; 34 | } 35 | 36 | public static SharedPreferences getPreferences() { 37 | return PreferenceManager.getDefaultSharedPreferences(instace); 38 | } 39 | 40 | 41 | @Override 42 | public void onCreate() { 43 | super.onCreate(); 44 | instace = this; 45 | SyncService.startAlarm(this); 46 | getBus().register(this); 47 | } 48 | 49 | @Override 50 | public void onTerminate() { 51 | super.onTerminate(); 52 | instace = null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/edit_heading_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 16 | 17 | 22 | 23 | 29 | 30 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/AgendaFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui; 2 | 3 | import android.view.Menu; 4 | import android.view.MenuInflater; 5 | 6 | import com.hdweiss.morgand.data.dao.OrgNode; 7 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 8 | import com.hdweiss.morgand.events.DataUpdatedEvent; 9 | import com.hdweiss.morgand.gui.outline.OutlineAdapter; 10 | import com.hdweiss.morgand.gui.outline.OutlineFragment; 11 | import com.squareup.otto.Subscribe; 12 | 13 | import java.sql.SQLException; 14 | import java.util.List; 15 | 16 | public class AgendaFragment extends OutlineFragment { 17 | 18 | @Override 19 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 20 | // Empty options menu for now. 21 | } 22 | 23 | @Override 24 | public void onPrepareOptionsMenu(Menu menu) { 25 | // Empty options menu for now. 26 | } 27 | 28 | @Subscribe 29 | public void refreshView(DataUpdatedEvent event) { 30 | refreshView(); 31 | } 32 | 33 | @Override 34 | protected void refreshView() { 35 | try { 36 | String query = getArguments().getString("query", "TODO%"); 37 | List orgNodes = OrgNodeRepository.queryBuilder().where().like(OrgNode.TITLE_FIELD_NAME, query) 38 | .and().not().like(OrgNode.FILE_FIELD_NAME, "%mOrgAnd.wiki/Development/Todo.org").query(); 39 | listView.setData(orgNodes); 40 | ((OutlineAdapter) listView.getAdapter()).setAgendaMode(true); 41 | } catch (SQLException e) { 42 | e.printStackTrace(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/calendar/CalendarEntriesParser.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.calendar; 2 | 3 | import android.database.Cursor; 4 | import android.provider.CalendarContract; 5 | 6 | public class CalendarEntriesParser { 7 | 8 | private int idColumn; 9 | private int dtStartColumn; 10 | private int dtEndColumn; 11 | private int titleColumn; 12 | private int descriptionColumn; 13 | private int locationColumn; 14 | private int allDayColumn; 15 | 16 | public CalendarEntriesParser(Cursor cursor) { 17 | dtStartColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.DTSTART); 18 | dtEndColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.DTEND); 19 | titleColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.TITLE); 20 | idColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events._ID); 21 | descriptionColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.DESCRIPTION); 22 | locationColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.EVENT_LOCATION); 23 | allDayColumn = cursor.getColumnIndexOrThrow(CalendarContract.Events.ALL_DAY); 24 | } 25 | 26 | public CalendarEntry getEntryFromCursor(Cursor cursor) { 27 | CalendarEntry entry = new CalendarEntry(); 28 | 29 | entry.dtStart = cursor.getLong(dtStartColumn); 30 | entry.dtEnd = cursor.getLong(dtEndColumn); 31 | entry.title = cursor.getString(titleColumn); 32 | entry.id = cursor.getLong(idColumn); 33 | entry.description = cursor.getString(descriptionColumn); 34 | entry.location = cursor.getString(locationColumn); 35 | entry.allDay = cursor.getInt(allDayColumn); 36 | 37 | return entry; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /mOrgAnd/src/androidTest/java/com/hdweiss/morgand/test/JGitWrapperTests.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.test; 2 | 3 | import android.content.SharedPreferences; 4 | import android.preference.PreferenceManager; 5 | import android.test.AndroidTestCase; 6 | 7 | import com.hdweiss.morgand.synchronizer.git.JGitWrapper; 8 | 9 | import java.io.File; 10 | 11 | public class JGitWrapperTests extends AndroidTestCase { 12 | 13 | private String localPath; 14 | 15 | private final String testFile = "README.md"; 16 | 17 | private JGitWrapper jGitWrapper; 18 | 19 | @Override 20 | protected void setUp() throws Exception { 21 | super.setUp(); 22 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); 23 | this.localPath = preferences.getString("git_local_path", ""); 24 | jGitWrapper = new JGitWrapper(preferences); 25 | } 26 | 27 | @Override 28 | protected void tearDown() throws Exception { 29 | super.tearDown(); 30 | jGitWrapper.getGit(null).close(); 31 | } 32 | 33 | 34 | public void testGitSetup() throws Exception { 35 | assertTrue(new File(localPath).exists()); 36 | assertTrue(new File(localPath + "/.git").exists()); 37 | assertTrue(jGitWrapper.getGit(null).branchList().call().size() > 0); 38 | } 39 | 40 | public void testCommitAndPush() throws Exception { 41 | String orgContents = TestUtils.readFileAsString(localPath + "/" + testFile); 42 | TestUtils.writeStringAsFile(orgContents + "\nmorgand", localPath + "/" + testFile); 43 | jGitWrapper.commitAllChanges("Automatic commit"); 44 | jGitWrapper.updateChanges(null); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/CalendarUtils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data; 2 | 3 | import android.text.format.DateUtils; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | import java.util.TimeZone; 9 | 10 | public class CalendarUtils { 11 | 12 | public static final SimpleDateFormat dateTimeformatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 13 | public static final SimpleDateFormat dateformatter = new SimpleDateFormat("yyyy-MM-dd"); 14 | 15 | 16 | public static String formatDate(long dtStart, long dtEnd, boolean allDay) { 17 | String date; 18 | 19 | if (allDay) 20 | date = dateformatter.format(new Date(dtStart)); 21 | else 22 | date = dateTimeformatter.format(new Date(dtStart)); 23 | 24 | if (dtEnd > 0 && dtStart != dtEnd) { 25 | long timeDiff = dtEnd - dtStart; 26 | 27 | if(timeDiff <= DateUtils.DAY_IN_MILLIS) { 28 | SimpleDateFormat timeformatter = new SimpleDateFormat("HH:mm"); 29 | String endTime = timeformatter.format(new Date(dtEnd)); 30 | 31 | date += "-" + endTime; 32 | } 33 | } 34 | 35 | return "<" + date + ">"; 36 | } 37 | 38 | public static long getDayInUTC(long time) { 39 | Calendar cal = Calendar.getInstance(); 40 | cal.setTimeInMillis(time); 41 | cal.set(Calendar.HOUR, 0); 42 | cal.set(Calendar.MINUTE, 0); 43 | cal.set(Calendar.SECOND, 0); 44 | cal.set(Calendar.MILLISECOND, 0); 45 | cal.setTimeZone(TimeZone.getTimeZone("UTC")); 46 | return cal.getTimeInMillis(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/dao/OrgFile.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data.dao; 2 | 3 | import android.content.Context; 4 | 5 | import com.hdweiss.morgand.Application; 6 | import com.j256.ormlite.android.apptools.OpenHelperManager; 7 | import com.j256.ormlite.dao.RuntimeExceptionDao; 8 | import com.j256.ormlite.field.DatabaseField; 9 | import com.j256.ormlite.table.DatabaseTable; 10 | 11 | import java.sql.SQLException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @DatabaseTable(tableName = "OrgFiles") 16 | public class OrgFile { 17 | 18 | public static final String AGENDA_FILE_ALIAS = "Agenda Views"; 19 | 20 | @DatabaseField(id = true) 21 | public String path = ""; 22 | 23 | @DatabaseField 24 | public long lastModified; 25 | 26 | public static final String NODE_FIELD_NAME = "node"; 27 | @DatabaseField(foreign = true, columnName = NODE_FIELD_NAME) 28 | public OrgNode node; 29 | 30 | public static RuntimeExceptionDao getDao() { 31 | Context context = Application.getInstace(); 32 | return OpenHelperManager.getHelper(context, DatabaseHelper.class).getRuntimeExceptionDao(OrgFile.class); 33 | } 34 | 35 | public static List getAllFiles() { 36 | try { 37 | return getDao().queryBuilder().query(); 38 | } catch (SQLException e) { 39 | e.printStackTrace(); 40 | } 41 | 42 | return new ArrayList(); 43 | } 44 | 45 | public static void deleteAll() { 46 | try { 47 | getDao().deleteBuilder().delete(); 48 | } catch (SQLException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | public boolean isEditable() { 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/git/JGitConfigSessionFactory.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.git; 2 | 3 | 4 | import com.jcraft.jsch.JSch; 5 | import com.jcraft.jsch.JSchException; 6 | import com.jcraft.jsch.Session; 7 | 8 | import org.eclipse.jgit.transport.CredentialsProvider; 9 | import org.eclipse.jgit.transport.CredentialsProviderUserInfo; 10 | import org.eclipse.jgit.transport.JschConfigSessionFactory; 11 | import org.eclipse.jgit.transport.OpenSshConfig; 12 | import org.eclipse.jgit.util.FS; 13 | 14 | public class JGitConfigSessionFactory extends JschConfigSessionFactory { 15 | private final String username; 16 | private final String password; 17 | private final String keyLocation; 18 | 19 | public JGitConfigSessionFactory(String username, String password, String keyLocation) { 20 | super(); 21 | this.username = username; 22 | this.password = password; 23 | this.keyLocation = keyLocation; 24 | } 25 | 26 | @Override 27 | protected void configure(OpenSshConfig.Host host, Session session) { 28 | session.setConfig("StrictHostKeyChecking", "no"); // TODO Find out how to enable strict host checking 29 | 30 | // TODO Delete me 31 | // String knownHostsLocation = "/sdcard/morg/known_hosts"; 32 | // jSch.setKnownHosts(knownHostsLocation); 33 | 34 | CredentialsProvider provider = new JGitCredentialsProvider(username, password); 35 | session.setUserInfo(new CredentialsProviderUserInfo(session, provider)); 36 | } 37 | 38 | @Override 39 | protected JSch getJSch(OpenSshConfig.Host hc, FS fs) throws JSchException { 40 | JSch jSch = super.getJSch(hc, fs); 41 | jSch.removeAllIdentity(); 42 | if (!keyLocation.isEmpty()) 43 | jSch.addIdentity(keyLocation, password); 44 | return jSch; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mOrgAnd 5 | 6 | 7 | Location 8 | folder can\'t be read! 9 | New 10 | Select 11 | File name: 12 | Cancel 13 | Save 14 | No Data 15 | #ffff0000 16 | Error 17 | 18 | 19 | mOrgAnd 20 | Agenda 21 | Outline 22 | Synchronizing changes 23 | 24 | Settings 25 | Synchronize 26 | Save 27 | Set time 28 | 29 | Downloading wiki 30 | 31 | Synchronization failed 32 | Error copying key file to internal storage 33 | Error getting fingerprint from key file 34 | This key requires a passphrase 35 | View 36 | Capture 37 | Edit 38 | Delete node 39 | Clock in 40 | Archive 41 | No calendar 42 | 43 | 44 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/git/JGitCredentialsProvider.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.git; 2 | 3 | import org.eclipse.jgit.errors.UnsupportedCredentialItem; 4 | import org.eclipse.jgit.transport.CredentialItem; 5 | import org.eclipse.jgit.transport.CredentialsProvider; 6 | import org.eclipse.jgit.transport.URIish; 7 | 8 | public class JGitCredentialsProvider extends CredentialsProvider { 9 | private String username; 10 | private String password; 11 | 12 | public JGitCredentialsProvider(String username, String password) { 13 | this.username = username; 14 | this.password = password; 15 | } 16 | 17 | @Override 18 | public boolean isInteractive() { 19 | return false; 20 | } 21 | 22 | @Override // never gets called 23 | public boolean supports(CredentialItem... items) { 24 | return true; 25 | } 26 | 27 | @Override 28 | public boolean get(URIish uri, CredentialItem... items) 29 | throws UnsupportedCredentialItem { 30 | for (CredentialItem item : items) { 31 | if (item instanceof CredentialItem.Username) { 32 | ((CredentialItem.Username) item).setValue(username); 33 | } else if (item instanceof CredentialItem.Password) { 34 | ((CredentialItem.Password) item).setValue(password.toCharArray()); 35 | } else if (item instanceof CredentialItem.StringType) { 36 | ((CredentialItem.StringType) item).setValue(password); 37 | } else if (item instanceof CredentialItem.InformationalMessage) { 38 | throw new UnsupportedCredentialItem(uri, "Not supported"); 39 | } else if (item instanceof CredentialItem.YesNoType) { 40 | // TODO handle strict host key checking here 41 | throw new UnsupportedCredentialItem(uri, "Not supported"); 42 | } else { 43 | throw new UnsupportedCredentialItem(uri, "Not supported"); 44 | } 45 | } 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/MainPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentPagerAdapter; 8 | 9 | import com.hdweiss.morgand.Application; 10 | import com.hdweiss.morgand.R; 11 | import com.hdweiss.morgand.gui.outline.OutlineFragment; 12 | 13 | import java.util.Locale; 14 | 15 | public class MainPagerAdapter extends FragmentPagerAdapter { 16 | 17 | private Context context; 18 | 19 | public MainPagerAdapter(FragmentManager fm) { 20 | super(fm); 21 | this.context = Application.getInstace(); 22 | } 23 | 24 | @Override 25 | public Fragment getItem(int position) { 26 | Fragment fragment; 27 | Bundle argumentBundle = new Bundle(); 28 | 29 | switch (position) { 30 | case 0: 31 | fragment = new OutlineFragment(); 32 | break; 33 | 34 | case 1: 35 | fragment = new AgendaFragment(); 36 | argumentBundle.putString("query", "NEXT%"); 37 | break; 38 | 39 | case 2: 40 | fragment = new AgendaFragment(); 41 | argumentBundle.putString("query", "TODO%"); 42 | break; 43 | 44 | default: 45 | fragment = new OutlineFragment(); 46 | break; 47 | } 48 | 49 | fragment.setArguments(argumentBundle); 50 | return fragment; 51 | } 52 | 53 | @Override 54 | public int getCount() { 55 | return 3; 56 | } 57 | 58 | @Override 59 | public CharSequence getPageTitle(int position) { 60 | Locale l = Locale.getDefault(); 61 | switch (position) { 62 | case 0: 63 | return context.getString(R.string.title_outline).toUpperCase(l); 64 | case 1: 65 | return "NEXTs"; 66 | case 2: 67 | return "TODOs"; 68 | } 69 | return null; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/edit_date_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | 32 | 33 | 37 | 38 | 39 | 40 | 45 | 46 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/theme/DefaultTheme.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.theme; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | 6 | import com.hdweiss.morgand.settings.PreferenceUtils; 7 | 8 | public class DefaultTheme { 9 | 10 | public int gray = Color.GRAY; 11 | 12 | public int c0Black = Color.rgb(0x00, 0x00, 0x00); 13 | public int c1Red = Color.rgb(0xd0, 0x00, 0x00); 14 | public int c2Green = Color.rgb(0x00, 0xa0, 0x00); 15 | public int c3Yellow = Color.rgb(0xc0, 0x80, 0x00); 16 | public int c4Blue = Color.rgb(0x22, 0x22, 0xf0); 17 | public int c5Purple = Color.rgb(0xa0, 0x00, 0xa0); 18 | public int c6Cyan = Color.rgb(0x00, 0x80, 0x80); 19 | public int c7White = Color.rgb(0xc0, 0xc0, 0xc0); 20 | 21 | public int c9LRed = Color.rgb(0xff, 0x77, 0x77); 22 | public int caLGreen = Color.rgb(0x77, 0xff, 0x77); 23 | public int cbLYellow = Color.rgb(0xff, 0xff, 0x00); 24 | public int ccLBlue = Color.rgb(0x88, 0x88, 0xff); 25 | public int cdLPurple = Color.rgb(0xff, 0x00, 0xff); 26 | public int ceLCyan = Color.rgb(0x00, 0xff, 0xff); 27 | public int cfLWhite = Color.rgb(0xff, 0xff, 0xff); 28 | 29 | public int defaultForeground = Color.rgb(0xc0, 0xc0, 0xc0); 30 | public int defaultBackground = Color.rgb(0x00, 0x00, 0x00); 31 | public int settingsForeground = Color.rgb(0x69, 0x6e, 0x52); 32 | 33 | public int directoryForeground = Color.rgb(0x3f, 0x2a, 0xdd); 34 | 35 | public int todoKeyword = Color.rgb(0xd0, 0x00, 0x00); 36 | public int inactiveTodoKeyword = Color.rgb(0x00, 0xa0, 0x00); 37 | public int priority = Color.rgb(0xc0, 0x80, 0x00); 38 | 39 | public int drawer = Color.rgb(0x5a, 0x5a, 0x4b); 40 | 41 | public int[] levelColors; 42 | 43 | public String defaultFontColor = "white"; 44 | 45 | public DefaultTheme() { 46 | levelColors = new int[] { ccLBlue, c3Yellow, ceLCyan, c2Green, 47 | c5Purple, ccLBlue, c2Green, ccLBlue, c3Yellow, ceLCyan }; 48 | } 49 | 50 | 51 | public static DefaultTheme getTheme(Context context) { 52 | final String themeName = PreferenceUtils.getThemeName(); 53 | if(themeName.equals("Light")) 54 | return new WhiteTheme(); 55 | else if(themeName.equals("Monochrome")) 56 | return new MonoTheme(); 57 | else 58 | return new DefaultTheme(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/dao/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data.dao; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.util.Log; 6 | 7 | import com.hdweiss.morgand.Application; 8 | import com.j256.ormlite.android.apptools.OpenHelperManager; 9 | import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; 10 | import com.j256.ormlite.dao.RuntimeExceptionDao; 11 | import com.j256.ormlite.support.ConnectionSource; 12 | import com.j256.ormlite.table.TableUtils; 13 | 14 | import java.sql.SQLException; 15 | 16 | public class DatabaseHelper extends OrmLiteSqliteOpenHelper { 17 | 18 | private static final String DATABASE_NAME = "mOrgAnd.db"; 19 | private static final int DATABASE_VERSION = 11; 20 | 21 | public DatabaseHelper(Context context) { 22 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 23 | } 24 | 25 | @Override 26 | public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) { 27 | try { 28 | Log.i(DatabaseHelper.class.getName(), "onCreate"); 29 | TableUtils.createTable(connectionSource, OrgNode.class); 30 | TableUtils.createTable(connectionSource, OrgFile.class); 31 | } catch (SQLException e) { 32 | Log.e(DatabaseHelper.class.getName(), "Can't create database", e); 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | 38 | @Override 39 | public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { 40 | try { 41 | Log.i(DatabaseHelper.class.getName(), "onUpgrade"); 42 | TableUtils.dropTable(connectionSource, OrgNode.class, true); 43 | TableUtils.dropTable(connectionSource, OrgFile.class, true); 44 | 45 | onCreate(db, connectionSource); 46 | } catch (SQLException e) { 47 | Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e); 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | 53 | public static RuntimeExceptionDao getOrgNodeDao() { 54 | Context context = Application.getInstace(); 55 | return OpenHelperManager.getHelper(context, DatabaseHelper.class).getRuntimeExceptionDao(OrgNode.class); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_data_sync.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | 27 | 28 | 33 | 34 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /mOrgAnd/src/androidTest/java/com/hdweiss/morgand/test/OrgFileWriterTests.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.test; 2 | 3 | import android.test.AndroidTestCase; 4 | 5 | import com.hdweiss.morgand.synchronizer.writer.OrgFileWriter; 6 | 7 | import junit.framework.Assert; 8 | 9 | import java.util.ArrayList; 10 | 11 | public class OrgFileWriterTests extends AndroidTestCase { 12 | 13 | private OrgFileWriter writer; 14 | private OrgFileTestScenario scenario; 15 | 16 | @Override 17 | protected void setUp() throws Exception { 18 | super.setUp(); 19 | scenario = new OrgFileTestScenario(); 20 | ArrayList fileCopy = new ArrayList(scenario.file); 21 | writer = new OrgFileWriter(fileCopy); 22 | } 23 | 24 | public void testDeleteHeading() { 25 | writer.delete(scenario.heading); 26 | 27 | Assert.assertEquals(debugString(), 2, writer.fileContent.size()); 28 | } 29 | 30 | 31 | public void testOverwriteSubheading() { 32 | OrgNodeStubbed subheading = scenario.subheading; 33 | subheading.title = "** new title"; 34 | writer.overwrite(subheading); 35 | 36 | Assert.assertEquals(debugString(), scenario.file.size(), writer.fileContent.size()); 37 | 38 | String writtenLine = writer.fileContent.get(subheading.lineNumber - 1); 39 | Assert.assertEquals(debugString(), subheading.title, writtenLine); 40 | } 41 | 42 | public void testOverwriteContent() { 43 | OrgNodeStubbed content = scenario.content; 44 | content.title = "test"; 45 | 46 | writer.overwrite(content); 47 | 48 | Assert.assertEquals(debugString(), scenario.file.size() - 1, writer.fileContent.size()); 49 | 50 | String writtenLine = writer.fileContent.get(content.lineNumber - 1); 51 | Assert.assertEquals(debugString(), content.title, writtenLine); 52 | } 53 | 54 | public void testAddSubheading() { 55 | OrgNodeStubbed parent = scenario.heading; 56 | 57 | OrgNodeStubbed child = new OrgNodeStubbed(-1); 58 | child.title = "** added child"; 59 | child.parent = parent; 60 | 61 | writer.add(child); 62 | 63 | Assert.assertEquals(debugString(), scenario.file.size() + 1, writer.fileContent.size()); 64 | 65 | int expectedIndex = scenario.heading2.lineNumber - 2; 66 | String writtenLine = writer.fileContent.get(expectedIndex); 67 | 68 | Assert.assertEquals(debugString(), child.title, writtenLine); 69 | } 70 | 71 | 72 | private String debugString() { 73 | String message = "Before:\n" + scenario.file.toString() + 74 | "\nAfter:\n" + writer.fileContent.toString(); 75 | return message; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /mOrgAnd/src/androidTest/java/com/hdweiss/morgand/test/OrgFileTestScenario.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.test; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class OrgFileTestScenario { 6 | 7 | public ArrayList file; 8 | 9 | public OrgNodeStubbed heading; 10 | public OrgNodeStubbed subheading; 11 | public OrgNodeStubbed content; 12 | public OrgNodeStubbed subheading2; 13 | public OrgNodeStubbed content2; 14 | public OrgNodeStubbed subsubheading1; 15 | public OrgNodeStubbed subheading3; 16 | public OrgNodeStubbed heading2; 17 | public OrgNodeStubbed content3; 18 | 19 | public OrgFileTestScenario() { 20 | setup(); 21 | } 22 | 23 | public void setup() { 24 | file = new ArrayList(); 25 | 26 | file.add("* Heading"); 27 | heading = new OrgNodeStubbed(file.size()); 28 | 29 | file.add("** sub heading 1"); 30 | subheading = new OrgNodeStubbed(file.size()); 31 | file.add("content 1"); 32 | content = new OrgNodeStubbed(file.size()); 33 | file.add("content 1"); 34 | 35 | file.add("** sub heading 2"); 36 | subheading2 = new OrgNodeStubbed(file.size()); 37 | file.add("content 2"); 38 | content2 = new OrgNodeStubbed(file.size()); 39 | file.add("content 2"); 40 | 41 | file.add("*** sub sub heading 1"); 42 | subsubheading1 = new OrgNodeStubbed(file.size()); 43 | 44 | file.add("** sub heading 3"); 45 | subheading3 = new OrgNodeStubbed(file.size()); 46 | 47 | file.add("* heading 2"); 48 | heading2 = new OrgNodeStubbed(file.size()); 49 | file.add("contents 3"); 50 | content3 = new OrgNodeStubbed(file.size()); 51 | 52 | heading.nextNodeLineNumber = subheading.lineNumber; 53 | heading.siblingLineNumber = heading2.lineNumber; 54 | 55 | subheading.nextNodeLineNumber = content.lineNumber; 56 | subheading.siblingLineNumber = subheading2.lineNumber; 57 | 58 | content.nextNodeLineNumber = subheading2.lineNumber; 59 | content.siblingLineNumber = subheading2.lineNumber; 60 | 61 | subheading2.nextNodeLineNumber = content2.lineNumber; 62 | subheading2.siblingLineNumber = subheading3.lineNumber; 63 | 64 | content2.nextNodeLineNumber = subsubheading1.lineNumber; 65 | content2.siblingLineNumber = subsubheading1.lineNumber; 66 | 67 | subsubheading1.nextNodeLineNumber = subheading3.lineNumber; 68 | subsubheading1.siblingLineNumber = subheading3.lineNumber; 69 | 70 | heading2.nextNodeLineNumber = content3.lineNumber; 71 | heading2.siblingLineNumber = Integer.MAX_VALUE; 72 | 73 | content3.nextNodeLineNumber = Integer.MAX_VALUE; 74 | content3.siblingLineNumber = Integer.MAX_VALUE; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/OrgNodeUtils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.hdweiss.morgand.data.dao.OrgNode; 6 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 7 | 8 | import java.util.HashSet; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | public class OrgNodeUtils { 13 | 14 | public static final Pattern urlPattern = Pattern.compile("(?:\\[\\[([^\\]]+)\\](?:\\[([^\\]]+)\\])?\\])|(http(?:s?)://\\S+)"); // Match [[url]], [[url][alias]] and http(s)://url 15 | public static final Pattern dateMatcher = Pattern.compile("((?:SCHEDULED:|DEADLINE:)\\s?)?<([^>]+)>" + "(?:\\s*--\\s*<([^>]+)>)?"); 16 | public static final Pattern headingPattern = Pattern.compile("([A-Z]+)(:?\\s(.+))?"); 17 | public static final Pattern prioritiesPattern = Pattern.compile("\\[#([^\\]]*)\\]"); 18 | 19 | public static String combineTags(String tags, String inheritedTags, HashSet excludedTags) { 20 | String combinedTags = ""; 21 | if (TextUtils.isEmpty(tags) == false) 22 | combinedTags += tags; 23 | 24 | if (TextUtils.isEmpty(inheritedTags) == false) 25 | combinedTags += inheritedTags; 26 | 27 | if (excludedTags == null || TextUtils.isEmpty(combinedTags)) 28 | return combinedTags; 29 | 30 | StringBuilder result = new StringBuilder(); 31 | for (String tag : combinedTags.split(":")) { 32 | if (excludedTags.contains(tag) == false && TextUtils.isEmpty(tag) == false) { 33 | result.append(tag); 34 | result.append(":"); 35 | } 36 | } 37 | 38 | if (!TextUtils.isEmpty(result)) 39 | result.deleteCharAt(result.lastIndexOf(":")); 40 | 41 | return result.toString(); 42 | } 43 | 44 | public static void toggleCheckbox(OrgNode node) { 45 | boolean checkedOff = node.title.contains("- [ ]"); 46 | if (checkedOff) { 47 | node.title = node.title.replaceFirst("-\\s\\[\\s\\]", "- [X]"); 48 | } else 49 | node.title = node.title.replaceFirst("-\\s\\[X\\]", "- [ ]"); 50 | 51 | OrgNodeRepository.update(node); 52 | 53 | if (node.parent == null) 54 | return; 55 | 56 | updateCheckboxCookie(node.parent, checkedOff); 57 | } 58 | 59 | private static void updateCheckboxCookie(OrgNode node, boolean increment) { 60 | try { 61 | Matcher matcher = Pattern.compile("\\[(\\d+)/(\\d+)\\]").matcher(node.title); 62 | if (matcher.find()) { 63 | int currentAmount = Integer.parseInt(matcher.group(1)); 64 | int total = Integer.parseInt(matcher.group(2)); 65 | currentAmount = increment ? currentAmount + 1 : currentAmount - 1; 66 | 67 | if (currentAmount < 0) 68 | return; 69 | 70 | node.title = node.title.replace(matcher.group(), "[" + currentAmount + "/" + total + "]"); 71 | OrgNodeRepository.update(node); 72 | } 73 | } catch (Exception ex) { 74 | ex.printStackTrace(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.net.ConnectivityManager; 6 | import android.net.NetworkInfo; 7 | import android.preference.PreferenceManager; 8 | 9 | import java.io.PrintWriter; 10 | import java.io.StringWriter; 11 | import java.util.ArrayList; 12 | import java.util.HashSet; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | public class Utils { 17 | public static String ExceptionTraceToString(Exception exception) { 18 | StringWriter sw = new StringWriter(); 19 | exception.printStackTrace(new PrintWriter(sw)); 20 | return sw.toString(); 21 | } 22 | 23 | public static boolean isWifiOnline(Context context) { 24 | try { 25 | ConnectivityManager conMan = (ConnectivityManager) context 26 | .getSystemService(Context.CONNECTIVITY_SERVICE); 27 | 28 | NetworkInfo.State wifi = conMan.getNetworkInfo(ConnectivityManager.TYPE_WIFI) 29 | .getState(); 30 | 31 | if (wifi == NetworkInfo.State.CONNECTED) 32 | return true; 33 | } catch (Exception ex) { 34 | ex.printStackTrace(); 35 | } 36 | return false; 37 | } 38 | 39 | public static boolean isMobileOnline(Context context) { 40 | try { 41 | ConnectivityManager conMan = (ConnectivityManager) context 42 | .getSystemService(Context.CONNECTIVITY_SERVICE); 43 | 44 | NetworkInfo.State mobile = conMan.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) 45 | .getState(); 46 | 47 | if (mobile == NetworkInfo.State.CONNECTED) 48 | return true; 49 | } catch (Exception ex) { 50 | ex.printStackTrace(); 51 | } 52 | return false; 53 | } 54 | 55 | 56 | public static boolean isNetworkOnline(Context context) { 57 | SharedPreferences prefs = PreferenceManager 58 | .getDefaultSharedPreferences(context); 59 | boolean wifiOnly = prefs.getBoolean("sync_wifi_only", false); 60 | 61 | if (wifiOnly) 62 | return isWifiOnline(context); 63 | else 64 | return isWifiOnline(context) 65 | || isMobileOnline(context); 66 | } 67 | 68 | public static boolean[] toPrimitiveArray(final List booleanList) { 69 | final boolean[] primitives = new boolean[booleanList.size()]; 70 | int index = 0; 71 | for (Boolean object : booleanList) { 72 | primitives[index++] = object; 73 | } 74 | return primitives; 75 | } 76 | 77 | public static List toList(HashSet set) { 78 | ArrayList list = new ArrayList(); 79 | if (set == null) 80 | return list; 81 | list.addAll(set); 82 | return list; 83 | } 84 | 85 | 86 | public static int getIteratorSize(Iterator iterator) { 87 | int i = 0; 88 | while (iterator.hasNext()) { 89 | iterator.next(); 90 | i++; 91 | } 92 | 93 | return i; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_calendar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 16 | 17 | 22 | 23 | 30 | 31 | 32 | 38 | 44 | 50 | 51 | 52 | 58 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/parser/SyncParserTask.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.parser; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | import android.util.Log; 7 | 8 | import com.hdweiss.morgand.Application; 9 | import com.hdweiss.morgand.data.dao.OrgFile; 10 | import com.hdweiss.morgand.events.DataUpdatedEvent; 11 | import com.hdweiss.morgand.events.SyncEvent; 12 | import com.hdweiss.morgand.gui.SynchronizerNotification; 13 | import com.hdweiss.morgand.settings.PreferenceUtils; 14 | import com.hdweiss.morgand.synchronizer.calendar.SyncCalendarTask; 15 | import com.hdweiss.morgand.utils.SafeAsyncTask; 16 | import com.hdweiss.morgand.utils.Utils; 17 | 18 | import java.util.ArrayList; 19 | 20 | public class SyncParserTask extends SafeAsyncTask { 21 | 22 | private ArrayList modifiedFiles; 23 | 24 | public SyncParserTask(Context context) { 25 | super(context, ReportMode.Log); 26 | } 27 | 28 | @Override 29 | protected Void safeDoInBackground(Void... voids) throws Exception { 30 | Log.d("Parser", "Started synchronization"); 31 | 32 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 33 | String localRepoPath = preferences.getString("git_local_path", ""); 34 | 35 | publishProgress(new SyncEvent(SyncEvent.State.Progress, 0)); 36 | 37 | OrgRepository repository = new OrgRepository(localRepoPath); 38 | modifiedFiles = repository.getModifiedFiles(); 39 | 40 | if (modifiedFiles.isEmpty()) { 41 | Log.d("Parser", "No modified files"); 42 | } 43 | 44 | int fileIndex = 0; 45 | for(OrgFile orgFile: modifiedFiles) { 46 | fileIndex++; 47 | 48 | Log.d("Parser", "Parsing " + orgFile.path); 49 | new OrgFileParser().parse(orgFile); 50 | 51 | int progress = (100 / modifiedFiles.size()) * fileIndex; 52 | publishProgress(new SyncEvent(SyncEvent.State.Progress, progress, orgFile.path)); 53 | } 54 | 55 | Log.d("Parser", "Ended synchronization"); 56 | return null; 57 | } 58 | 59 | @Override 60 | protected void onProgressUpdate(SyncEvent... events) { 61 | super.onProgressUpdate(events); 62 | 63 | for(SyncEvent event: events) 64 | Application.getBus().post(event); 65 | 66 | Application.getBus().post(new DataUpdatedEvent()); 67 | } 68 | 69 | @Override 70 | protected void onSuccess(Void aVoid) { 71 | String[] filenames = new String[modifiedFiles.size()]; 72 | for(int i = 0; i < modifiedFiles.size(); i++) { 73 | filenames[i] = modifiedFiles.get(i).path; 74 | } 75 | 76 | if (PreferenceUtils.syncCalendar()) 77 | new SyncCalendarTask(context).execute(filenames); 78 | } 79 | 80 | @Override 81 | protected void onCleanup() { 82 | Application.getBus().post(new SyncEvent(SyncEvent.State.Done)); 83 | } 84 | 85 | @Override 86 | protected void onError() { 87 | SynchronizerNotification notification = new SynchronizerNotification(context); 88 | notification.errorNotification(exception.getMessage() + "\n" + Utils.ExceptionTraceToString(exception)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/OrgCalendarEntry.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data; 2 | 3 | import android.text.TextUtils; 4 | import android.text.format.DateUtils; 5 | import android.util.Log; 6 | 7 | import java.text.ParseException; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | public class OrgCalendarEntry { 12 | 13 | public long beginTime = 0; 14 | public long endTime = 0; 15 | public int allDay = 0; 16 | public String type = ""; 17 | public String title = ""; 18 | 19 | 20 | private static final String datePattern = "(\\d{1,2}\\:\\d{2})"; // d:dd or dd:dd 21 | private static final Pattern schedulePattern = Pattern 22 | .compile("(\\d{4}-\\d{2}-\\d{2})" // YYYY-MM-DD 23 | + "(?:[^\\d]*)" // Strip out month 24 | + datePattern + "?" // Begin time 25 | + "(?:\\-" + datePattern + ")?"); // "-" followed by end time 26 | 27 | private static final int DATE = 1; 28 | private static final int BEGIN_TIME = 2; 29 | private static final int END_TIME = 3; 30 | 31 | 32 | public OrgCalendarEntry(String date) throws IllegalArgumentException { 33 | Matcher schedule = schedulePattern.matcher(date); 34 | 35 | if (schedule.find()) { 36 | try { 37 | if(schedule.group(BEGIN_TIME) == null) { // event is an entire day event 38 | this.beginTime = CalendarUtils.dateformatter.parse(schedule.group(DATE)).getTime(); 39 | 40 | // All day events need to be in UTC and end time is exactly one day after 41 | this.beginTime = CalendarUtils.getDayInUTC(beginTime); 42 | this.endTime = this.beginTime + DateUtils.DAY_IN_MILLIS; 43 | this.allDay = 1; 44 | } 45 | else if (schedule.group(BEGIN_TIME) != null && schedule.group(END_TIME) != null) { 46 | this.beginTime = CalendarUtils.dateTimeformatter.parse(schedule.group(DATE) + " " + schedule.group(BEGIN_TIME)).getTime(); 47 | this.endTime = CalendarUtils.dateTimeformatter.parse(schedule.group(DATE) + " " + schedule.group(END_TIME)).getTime(); 48 | this.allDay = 0; 49 | } else if(schedule.group(BEGIN_TIME) != null) { 50 | this.beginTime = CalendarUtils.dateTimeformatter.parse(schedule.group(DATE) + " " + schedule.group(BEGIN_TIME)).getTime(); 51 | this.endTime = beginTime + DateUtils.HOUR_IN_MILLIS; 52 | this.allDay = 0; 53 | } 54 | 55 | return; 56 | } catch (ParseException e) { 57 | Log.w("MobileOrg", "Unable to parse schedule: " + date); 58 | } 59 | } else 60 | throw new IllegalArgumentException("Could not create date out of entry"); 61 | } 62 | 63 | 64 | 65 | /** 66 | * Whether an event is in the past. True if event ended 24 hours ago or 67 | * sometime in the future. 68 | */ 69 | public boolean isInPast() { 70 | return System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS >= endTime; 71 | } 72 | 73 | public void setTitle(String title) { 74 | this.title = title; 75 | } 76 | 77 | 78 | public String getCalendarTitle() { 79 | String formatedType = this.type; 80 | if (type.startsWith("SCHEDULED")) 81 | formatedType = "SC"; 82 | else if (type.startsWith("DEADLINE")) 83 | formatedType = "DL"; 84 | 85 | if (TextUtils.isEmpty(formatedType)) 86 | return this.title; 87 | else 88 | return formatedType + ": " + this.title; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/writer/SyncWriterTask.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.writer; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.hdweiss.morgand.data.dao.OrgFile; 7 | import com.hdweiss.morgand.data.dao.OrgNode; 8 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 9 | import com.hdweiss.morgand.events.SyncEvent; 10 | import com.hdweiss.morgand.gui.SynchronizerNotification; 11 | import com.hdweiss.morgand.settings.PreferenceUtils; 12 | import com.hdweiss.morgand.synchronizer.git.SyncGitTask; 13 | import com.hdweiss.morgand.utils.SafeAsyncTask; 14 | import com.hdweiss.morgand.utils.Utils; 15 | 16 | import java.util.List; 17 | 18 | public class SyncWriterTask extends SafeAsyncTask { 19 | 20 | public SyncWriterTask(Context context) { 21 | super(context, ReportMode.Log); 22 | } 23 | 24 | @Override 25 | protected Void safeDoInBackground(OrgFile... files) throws Exception { 26 | Log.d("Writer", "Started synchronization"); 27 | 28 | if (files.length > 0) { 29 | for (OrgFile file : files) 30 | writeChanges(file); 31 | } else { 32 | for(OrgFile file : OrgFile.getAllFiles()) 33 | writeChanges(file); 34 | } 35 | 36 | Log.d("Writer", "Ended synchronization"); 37 | return null; 38 | } 39 | 40 | private void writeChanges(OrgFile file) throws Exception { 41 | OrgFileWriter writer = new OrgFileWriter(file); 42 | 43 | List dirtyNodes = OrgNodeRepository.getDirtyNodes(file); 44 | 45 | if (dirtyNodes.isEmpty()) 46 | return; 47 | 48 | Log.d("Writer", "Writing changes to " + file.path); 49 | 50 | for(OrgNode node: dirtyNodes) 51 | applyChanges(writer, node); 52 | 53 | writer.write(); 54 | 55 | for(OrgNode node: dirtyNodes) { 56 | node.state = OrgNode.State.Clean; 57 | OrgNodeRepository.update(node); 58 | } 59 | } 60 | 61 | private void applyChanges(OrgFileWriter writer, OrgNode node) { 62 | if (node.isNodeWritable() == false) { 63 | Log.d("Writer", "Node not writable " + node.getTitle()); 64 | return; 65 | } 66 | 67 | if (node.type == OrgNode.Type.File || node.type == OrgNode.Type.Directory) 68 | return; // We don't handle deletion of files or directories 69 | 70 | switch (node.state) { 71 | case Added: 72 | writer.add(node); 73 | break; 74 | 75 | case Deleted: 76 | writer.delete(node); 77 | break; 78 | 79 | case Updated: 80 | writer.overwrite(node); 81 | break; 82 | 83 | default: 84 | break; 85 | } 86 | } 87 | 88 | @Override 89 | protected void onSuccess(Void aVoid) { 90 | if (PreferenceUtils.syncMode().equals("git")) 91 | new SyncGitTask(context).execute(); 92 | } 93 | 94 | @Override 95 | protected void onError() { 96 | SynchronizerNotification notification = new SynchronizerNotification(context); 97 | notification.errorNotification(exception.getMessage() + "\n" + Utils.ExceptionTraceToString(exception)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/writer/OrgFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.writer; 2 | 3 | import com.hdweiss.morgand.data.dao.OrgFile; 4 | import com.hdweiss.morgand.data.dao.OrgNode; 5 | import com.hdweiss.morgand.utils.FileUtils; 6 | 7 | import java.io.BufferedWriter; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | 12 | public class OrgFileWriter { 13 | 14 | private final OrgFile orgFile; 15 | public ArrayList fileContent; 16 | 17 | public OrgFileWriter(OrgFile orgFile) throws IOException { 18 | this.orgFile = orgFile; 19 | fileContent = FileUtils.fileToArrayList(orgFile.path); 20 | } 21 | 22 | /** 23 | * Constructor for unit testing. 24 | */ 25 | public OrgFileWriter(ArrayList fileContent) { 26 | this.orgFile = null; 27 | this.fileContent = fileContent; 28 | } 29 | 30 | 31 | public void write() throws IOException { 32 | BufferedWriter writer = new BufferedWriter(new FileWriter(orgFile.path, false)); 33 | for(String line: fileContent) 34 | writer.write(line + "\n"); 35 | writer.close(); 36 | } 37 | 38 | 39 | public void add(OrgNode node) { 40 | if (node == null) throw new IllegalArgumentException("Got null node as argument"); 41 | if (node.parent == null) throw new IllegalArgumentException("Got node with null parent as argument: " + node.title); 42 | if (node.parent.type == OrgNode.Type.Directory) throw new IllegalArgumentException("Got node with invalid parent type"); 43 | 44 | int index = node.parent.getSiblingLineNumber() - 1; 45 | if (index < 0) throw new IllegalArgumentException("Got node with parent lineNumber less than 0: " + node.parent.title); 46 | 47 | add(index, node.toStringRecursively()); 48 | } 49 | 50 | public void delete(OrgNode node) { 51 | if (node == null) throw new IllegalArgumentException("Got null node as argument"); 52 | if (node.lineNumber < 0) throw new IllegalArgumentException("Node's lineNumber can't be less than 0: " + node.title); 53 | 54 | int startIndex = node.lineNumber - 1; 55 | int endIndex = node.getSiblingLineNumber() - 1; 56 | removeRange(startIndex, endIndex); 57 | } 58 | 59 | public void overwrite(OrgNode node) { 60 | int startIndex = node.lineNumber - 1; 61 | int endIndex = node.getNextNodeLineNumber() - 1; 62 | removeRange(startIndex, endIndex); 63 | add(startIndex, node.toString()); 64 | } 65 | 66 | 67 | private void removeRange(final int from, final int to) { 68 | if (from < 0 || from > to) throw new IllegalArgumentException("Can't remove range from=" + from + " to=" + to + " fileContent.size()=" + fileContent.size()); 69 | 70 | for (int linesToDelete = to - from; linesToDelete > 0 && from < fileContent.size(); linesToDelete--) 71 | fileContent.remove(from); 72 | } 73 | 74 | private void add(final int index, final String content) { 75 | if (index < 0) throw new IllegalArgumentException("Can't add contents with negative index index=" + index + ", content=" + content); 76 | 77 | if (index <= fileContent.size()) 78 | fileContent.add(index, content); 79 | else 80 | fileContent.add(content); 81 | } 82 | 83 | public String toString() { 84 | return fileContent.toString(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/xml/pref_git.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 32 | 33 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 63 | 72 | 73 | 82 | 83 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/OrgNodeTimeDate.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.Calendar; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public class OrgNodeTimeDate { 10 | private TYPE type = TYPE.Scheduled; 11 | 12 | private int year = -1; 13 | private int monthOfYear = -1; 14 | private int dayOfMonth = -1; 15 | private int startTimeOfDay = -1; 16 | private int startMinute = -1; 17 | private int endTimeOfDay = -1; 18 | private int endMinute = -1; 19 | 20 | private enum TYPE { 21 | Scheduled, 22 | Deadline, 23 | Timestamp, 24 | InactiveTimestamp 25 | }; 26 | 27 | private OrgNodeTimeDate(TYPE type) { 28 | this.type = type; 29 | } 30 | 31 | 32 | private void setToCurrentDate() { 33 | final Calendar c = Calendar.getInstance(); 34 | this.year = c.get(Calendar.YEAR); 35 | this.monthOfYear = c.get(Calendar.MONTH) + 1; 36 | this.dayOfMonth = c.get(Calendar.DAY_OF_MONTH); 37 | } 38 | 39 | private static final Pattern schedulePattern = Pattern 40 | .compile("((\\d{4})-(\\d{1,2})-(\\d{1,2}))(?:[^\\d]*)" 41 | + "((\\d{1,2})\\:(\\d{2}))?(-((\\d{1,2})\\:(\\d{2})))?"); 42 | private void parseDate(String date) { 43 | if(date == null) 44 | return; 45 | 46 | Matcher propm = schedulePattern.matcher(date); 47 | 48 | if (propm.find()) { 49 | try { 50 | year = Integer.parseInt(propm.group(2)); 51 | monthOfYear = Integer.parseInt(propm.group(3)); 52 | dayOfMonth = Integer.parseInt(propm.group(4)); 53 | 54 | startTimeOfDay = Integer.parseInt(propm.group(6)); 55 | startMinute = Integer.parseInt(propm.group(7)); 56 | 57 | endTimeOfDay = Integer.parseInt(propm.group(10)); 58 | endMinute = Integer.parseInt(propm.group(11)); 59 | } catch (NumberFormatException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | } 64 | 65 | 66 | private String getDate() { 67 | return String.format("%d-%02d-%02d", year, monthOfYear, dayOfMonth); 68 | } 69 | 70 | private String getStartTime() { 71 | return String.format("%02d:%02d", startTimeOfDay, startMinute); 72 | } 73 | 74 | private String getEndTime() { 75 | return String.format("%02d:%02d", endTimeOfDay, endMinute); 76 | } 77 | 78 | 79 | private String toString2() { 80 | return getDate().toString() + getStartTimeFormated() + getEndTimeFormated(); 81 | } 82 | 83 | public String toFormatedString() { 84 | return formatDate(type, getDate()); 85 | } 86 | 87 | 88 | private String getStartTimeFormated() { 89 | String time = getStartTime().toString(); 90 | 91 | if (startTimeOfDay == -1 92 | || startMinute == -1 || TextUtils.isEmpty(time)) 93 | return ""; 94 | else 95 | return " " + time; 96 | } 97 | 98 | private String getEndTimeFormated() { 99 | String time = getEndTime().toString(); 100 | 101 | if (endTimeOfDay == -1 102 | || endMinute == -1 || TextUtils.isEmpty(time)) 103 | return ""; 104 | else 105 | return "-" + time; 106 | } 107 | 108 | 109 | private static String typeToFormated(TYPE type) { 110 | switch (type) { 111 | case Scheduled: 112 | return "SCHEDULED: "; 113 | case Deadline: 114 | return "DEADLINE: "; 115 | case Timestamp: 116 | return ""; 117 | default: 118 | return ""; 119 | } 120 | } 121 | 122 | private String formatDate(TYPE type, String timestamp) { 123 | if (TextUtils.isEmpty(timestamp)) 124 | return ""; 125 | else { 126 | return OrgNodeTimeDate.typeToFormated(type) + "<" + timestamp + ">"; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/EditHeadingFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit; 2 | 3 | import android.os.Bundle; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.view.WindowManager; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.AutoCompleteTextView; 11 | import android.widget.TextView; 12 | 13 | import com.hdweiss.morgand.R; 14 | import com.hdweiss.morgand.data.dao.OrgNode; 15 | import com.hdweiss.morgand.settings.PreferenceUtils; 16 | import com.hdweiss.morgand.utils.Utils; 17 | 18 | import java.util.HashSet; 19 | 20 | public class EditHeadingFragment extends BaseEditFragment { 21 | 22 | private AutoCompleteTextView headingView; 23 | private TextView inheritedTagsView; 24 | private AutoCompleteTextView tagsView; 25 | 26 | @Override 27 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 28 | View view = inflater.inflate(R.layout.edit_heading_fragment, container, false); 29 | 30 | tagsView = (AutoCompleteTextView) view.findViewById(R.id.tags); 31 | inheritedTagsView = (TextView) view.findViewById(R.id.inheritedTags); 32 | 33 | headingView = (AutoCompleteTextView) view.findViewById(R.id.heading); 34 | headingView.setOnEditorActionListener(this); 35 | headingView.setThreshold(0); 36 | headingView.requestFocus(); 37 | getDialog().getWindow().setSoftInputMode( 38 | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 39 | 40 | return view; 41 | } 42 | 43 | @Override 44 | public void onViewCreated(View view, Bundle savedInstanceState) { 45 | super.onViewCreated(view, savedInstanceState); 46 | 47 | if (controller == null) 48 | return; 49 | 50 | OrgNode node = controller.getNode(); 51 | populateView(node.getTitle(), node.tags, node.inheritedTags); 52 | populateAutocompletion(); 53 | 54 | switch (controller.getMode()) { 55 | case Add: 56 | getDialog().setTitle(R.string.action_capture); 57 | break; 58 | 59 | case Edit: 60 | getDialog().setTitle(R.string.action_edit); 61 | break; 62 | } 63 | } 64 | 65 | private void populateView(String heading, String tags, String inheritedTags) { 66 | if (heading != null) { 67 | headingView.setText(heading); 68 | headingView.setSelection(headingView.getText().length()); 69 | } 70 | 71 | if (TextUtils.isEmpty(inheritedTags)) 72 | inheritedTagsView.setVisibility(View.GONE); 73 | else { 74 | inheritedTagsView.setVisibility(View.VISIBLE); 75 | inheritedTagsView.setText(inheritedTags); 76 | } 77 | 78 | if (tags != null) 79 | tagsView.setText(tags); 80 | } 81 | 82 | private void populateAutocompletion() { 83 | HashSet todoKeywords = PreferenceUtils.getAllTodoKeywords(); 84 | if (todoKeywords.size() == 0) 85 | return; 86 | 87 | ArrayAdapter adapter = new ArrayAdapter(getActivity(), 88 | android.R.layout.simple_dropdown_item_1line, Utils.toList(todoKeywords)); 89 | headingView.setAdapter(adapter); 90 | } 91 | 92 | @Override 93 | public OrgNode getEditedNode() { 94 | OrgNode editNode = controller.getNode(); 95 | editNode.title = headingView.getText().toString(); 96 | editNode.tags = tagsView.getText().toString(); 97 | return editNode; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/settings/PreferenceUtils.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.settings; 2 | 3 | import android.content.SharedPreferences; 4 | import android.preference.PreferenceManager; 5 | import android.text.TextUtils; 6 | 7 | import com.hdweiss.morgand.Application; 8 | import com.hdweiss.morgand.utils.FileUtils; 9 | 10 | import java.io.File; 11 | import java.util.HashSet; 12 | 13 | public class PreferenceUtils { 14 | 15 | private static SharedPreferences getPrefs() { 16 | return PreferenceManager.getDefaultSharedPreferences(Application.getInstace()); 17 | } 18 | 19 | public static void set(String key, String value) { 20 | SharedPreferences.Editor editor = getPrefs().edit(); 21 | editor.putString(key, value); 22 | editor.commit(); 23 | } 24 | 25 | public static String getThemeName() { 26 | return "Light"; 27 | } 28 | 29 | public static HashSet getExcludedTags() { 30 | return new HashSet(); 31 | } 32 | 33 | 34 | public static HashSet getInactiveTodoKeywords() { 35 | return getHashSetFromPreferenceString("todo_inactive", "DONE", ":"); 36 | } 37 | 38 | public static HashSet getActiveTodoKeywords() { 39 | return getHashSetFromPreferenceString("todo_active", "TODO:NEXT", ":"); 40 | } 41 | 42 | public static HashSet getAllTodoKeywords() { 43 | HashSet todoKeywords = getActiveTodoKeywords(); 44 | todoKeywords.addAll(getInactiveTodoKeywords()); 45 | return todoKeywords; 46 | } 47 | 48 | public static HashSet getPriorties() { 49 | return getHashSetFromPreferenceString("priorities", "A:B:C", ":"); 50 | } 51 | 52 | private static HashSet getHashSetFromPreferenceString(final String key, final String defaultValue, final String delimiter) { 53 | HashSet keywordHashset = new HashSet(); 54 | 55 | String activeKeywords = getPrefs().getString(key, defaultValue); 56 | String[] keywords = activeKeywords.split(delimiter); 57 | for(String keyword: keywords) { 58 | if (TextUtils.isEmpty(keyword) == false) 59 | keywordHashset.add(keyword); 60 | } 61 | return keywordHashset; 62 | } 63 | 64 | public static boolean syncCalendar() { 65 | return getPrefs().getBoolean("calendar_enabled", false); 66 | } 67 | 68 | public static boolean showDrawers() { 69 | return getPrefs().getBoolean("show_drawers", false); 70 | } 71 | 72 | public static boolean showSettings() { 73 | return getPrefs().getBoolean("show_settings", false); 74 | } 75 | 76 | public static boolean outlineExpandAll() { 77 | return getPrefs().getBoolean("outline_expandall", false); 78 | } 79 | 80 | public static String syncMode() { 81 | return getPrefs().getString("sync_mode", "git"); 82 | } 83 | 84 | public static void setupGitToWiki() { 85 | SharedPreferences.Editor editor = getPrefs().edit(); 86 | editor.remove("git_username"); 87 | editor.remove("git_password"); 88 | editor.remove("git_key_path"); 89 | 90 | File externalDir = Application.getInstace().getExternalFilesDir(null); 91 | File file = new File(externalDir, "mOrgAnd.wiki"); 92 | 93 | if (file.exists()) 94 | FileUtils.deleteDirectory(file); 95 | 96 | editor.putString("sync_mode", "git"); 97 | editor.putString("git_local_path", file.getAbsolutePath()); 98 | editor.putString("git_url", "git://github.com/hdweiss/mOrgAnd.wiki"); 99 | editor.putString("git_branch", "master"); 100 | editor.commit(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/outline/OutlineActionMode.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.outline; 2 | 3 | import android.content.Context; 4 | import android.view.ActionMode; 5 | import android.view.Menu; 6 | import android.view.MenuInflater; 7 | import android.view.MenuItem; 8 | import android.widget.ListView; 9 | 10 | import com.hdweiss.morgand.data.dao.OrgNode; 11 | 12 | 13 | public class OutlineActionMode implements ActionMode.Callback { 14 | 15 | private Context context; 16 | 17 | private ListView list; 18 | private OutlineAdapter adapter; 19 | private int listPosition; 20 | private OrgNode node; 21 | 22 | public OutlineActionMode(Context context) { 23 | super(); 24 | this.context = context; 25 | } 26 | 27 | public void initActionMode(ListView list, int position, int restorePosition) { 28 | initActionMode(list, position); 29 | this.listPosition = restorePosition; 30 | } 31 | 32 | public void initActionMode(ListView list, int position) { 33 | list.setItemChecked(position, true); 34 | this.list = list; 35 | this.adapter = (OutlineAdapter) list.getAdapter(); 36 | this.listPosition = position; 37 | this.node = adapter.getItem(position); 38 | } 39 | 40 | @Override 41 | public void onDestroyActionMode(ActionMode mode) { 42 | this.list.setItemChecked(this.listPosition, true); 43 | } 44 | 45 | @Override 46 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 47 | MenuInflater inflater = mode.getMenuInflater(); 48 | 49 | // if (this.node != null && this.node.Id >= 0 && node.isEditable()) { 50 | // inflater.inflate(R.menu.outline_node, menu); 51 | // } 52 | // else if(this.node != null && this.node.type == OrgNode.Type.File) { 53 | // if(this.node.title.equals(OrgFile.AGENDA_FILE_ALIAS)) 54 | // inflater.inflate(R.menu.outline_file_uneditable, menu); 55 | // else 56 | // inflater.inflate(R.menu.outline_file, menu); 57 | // } else 58 | // inflater.inflate(R.menu.outline_node_uneditable, menu); 59 | // 60 | return true; 61 | } 62 | 63 | @Override 64 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 65 | return false; 66 | } 67 | 68 | @Override 69 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 70 | switch (item.getItemId()) { 71 | 72 | // case R.id.menu_edit: 73 | // runEditNodeActivity(node.Id, context); 74 | // break; 75 | // case R.id.menu_delete: 76 | // runDeleteNode(); 77 | // break; 78 | // case R.id.menu_delete_file: 79 | // runDeleteFileNode(); 80 | // break; 81 | // case R.id.menu_clockin: 82 | // runTimeClockingService(); 83 | // break; 84 | // case R.id.menu_archive: 85 | // runArchiveNode(false); 86 | // break; 87 | // case R.id.menu_view: 88 | // runViewNodeActivity(); 89 | // break; 90 | // 91 | // case R.id.menu_capturechild: 92 | // runCaptureActivity(node.Id, context); 93 | // break; 94 | // 95 | // default: 96 | // mode.finish(); 97 | // return false; 98 | } 99 | 100 | mode.finish(); 101 | return true; 102 | } 103 | 104 | 105 | public static void runEditNodeActivity(long nodeId, Context context) { 106 | 107 | } 108 | 109 | public static void runCaptureActivity(long id, Context context) { 110 | 111 | } 112 | 113 | private void runDeleteNode() { 114 | 115 | } 116 | 117 | private void runArchiveNode(final boolean archiveToSibling) { 118 | 119 | } 120 | 121 | private void archiveNode(boolean archiveToSibling) { 122 | 123 | } 124 | 125 | private void runDeleteFileNode() { 126 | 127 | } 128 | 129 | private void deleteFileNode() { 130 | 131 | } 132 | 133 | public static void runViewNodeActivity(long nodeId, Context context) { 134 | 135 | } 136 | 137 | private void runViewNodeActivity() { 138 | } 139 | 140 | private void runTimeClockingService() { 141 | 142 | } 143 | 144 | private void runRecover() { 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/file_dialog_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 17 | 19 | 22 | 25 | 26 | 27 | 28 | 33 | 35 | 37 | 38 | 40 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 56 | 58 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/BaseEditFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit; 2 | 3 | import android.app.Activity; 4 | import android.app.DialogFragment; 5 | import android.app.FragmentTransaction; 6 | import android.os.Bundle; 7 | import android.view.KeyEvent; 8 | import android.view.inputmethod.EditorInfo; 9 | import android.widget.TextView; 10 | 11 | import com.hdweiss.morgand.Application; 12 | import com.hdweiss.morgand.data.dao.OrgNode; 13 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 14 | import com.hdweiss.morgand.events.DataUpdatedEvent; 15 | import com.hdweiss.morgand.gui.edit.controller.AddController; 16 | import com.hdweiss.morgand.gui.edit.controller.BaseEditController; 17 | import com.hdweiss.morgand.gui.edit.controller.EditController; 18 | 19 | public abstract class BaseEditFragment extends DialogFragment implements TextView.OnEditorActionListener { 20 | 21 | public static BaseEditFragment getEditFragment(OrgNode node) { 22 | BaseEditFragment fragment; 23 | switch (node.type) { 24 | case Headline: 25 | fragment = new EditHeadingFragment(); 26 | break; 27 | 28 | case Date: 29 | fragment = new EditDateFragment(); 30 | break; 31 | 32 | default: 33 | fragment = new EditTextFragment(); 34 | break; 35 | } 36 | 37 | setFragmentArguments(fragment, node.Id, BaseEditController.EditMode.Edit); 38 | return fragment; 39 | } 40 | 41 | public static BaseEditFragment getAddFragment(OrgNode node) { 42 | BaseEditFragment fragment = new EditHeadingFragment(); 43 | setFragmentArguments(fragment, node.Id, BaseEditController.EditMode.Add); 44 | return fragment; 45 | } 46 | 47 | private static void setFragmentArguments(BaseEditFragment fragment, int nodeId, BaseEditController.EditMode mode) { 48 | Bundle argumentBundle = new Bundle(); 49 | argumentBundle.putInt("nodeId", nodeId); 50 | argumentBundle.putString("mode", mode.name()); 51 | 52 | fragment.setArguments(argumentBundle); 53 | } 54 | 55 | 56 | protected BaseEditController controller; 57 | 58 | @Override 59 | public void onCreate(Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | 62 | int nodeId = getArguments().getInt("nodeId", -1); 63 | if (nodeId == -1) 64 | throw new IllegalArgumentException("No nodeId given"); 65 | 66 | OrgNode node = OrgNodeRepository.queryForId(nodeId); 67 | 68 | String editModeString = getArguments().getString("mode", "edit"); 69 | BaseEditController.EditMode mode = BaseEditController.EditMode.valueOf(editModeString); 70 | 71 | switch (mode) { 72 | case Add: 73 | this.controller = new AddController(node, OrgNode.Type.Headline); 74 | break; 75 | 76 | case Edit: 77 | default: 78 | this.controller = new EditController(node); 79 | break; 80 | } 81 | } 82 | 83 | public abstract OrgNode getEditedNode(); 84 | 85 | 86 | public void show(Activity activity) { 87 | FragmentTransaction fragmentTransaction = activity.getFragmentManager().beginTransaction(); 88 | fragmentTransaction.add(this, "dialog"); 89 | fragmentTransaction.commit(); 90 | } 91 | 92 | @Override 93 | public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { 94 | if (EditorInfo.IME_ACTION_DONE == actionId) { 95 | if (controller != null) { 96 | OrgNode editedNode = getEditedNode(); 97 | controller.save(editedNode); 98 | Application.getBus().post(new DataUpdatedEvent()); 99 | } 100 | dismiss(); 101 | return true; 102 | } 103 | 104 | return false; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/git/SyncGitTask.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.git; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Build; 6 | import android.preference.PreferenceManager; 7 | import android.util.Log; 8 | 9 | import com.hdweiss.morgand.Application; 10 | import com.hdweiss.morgand.events.SyncEvent; 11 | import com.hdweiss.morgand.gui.SynchronizerNotification; 12 | import com.hdweiss.morgand.synchronizer.parser.SyncParserTask; 13 | import com.hdweiss.morgand.utils.SafeAsyncTask; 14 | import com.hdweiss.morgand.utils.Utils; 15 | 16 | import org.eclipse.jgit.lib.ProgressMonitor; 17 | 18 | public class SyncGitTask extends SafeAsyncTask { 19 | 20 | private JGitWrapper jGitWrapper; 21 | 22 | public SyncGitTask(Context context) { 23 | super(context, ReportMode.Toast); 24 | } 25 | 26 | @Override 27 | protected Void safeDoInBackground(Void... params) throws Exception { 28 | Log.d("Git", "Started synchronization"); 29 | 30 | if (Utils.isNetworkOnline(context) == false) { 31 | Log.d("Git", "Network is offline, aborting git synchronization"); 32 | return null; 33 | } 34 | 35 | publishProgress(new SyncEvent(SyncEvent.State.Intermediate)); 36 | 37 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 38 | jGitWrapper = new JGitWrapper(preferences); 39 | jGitWrapper.commitAllChanges(Build.MODEL + ": Automatic commit"); 40 | jGitWrapper.updateChanges(monitor); 41 | 42 | Log.d("Git", "Ended synchronization"); 43 | return null; 44 | } 45 | 46 | @Override 47 | protected void onProgressUpdate(SyncEvent... events) { 48 | super.onProgressUpdate(events); 49 | for(SyncEvent event: events) 50 | Application.getBus().post(event); 51 | } 52 | 53 | @Override 54 | protected void onSuccess(Void aVoid) { 55 | Application.getBus().post(new SyncEvent(SyncEvent.State.SecondaryProgress, 100)); 56 | new SyncParserTask(context).execute(); 57 | } 58 | 59 | @Override 60 | protected void onError() { 61 | Application.getBus().post(new SyncEvent(SyncEvent.State.Done)); 62 | SynchronizerNotification notification = new SynchronizerNotification(context); 63 | notification.errorNotification(exception.getLocalizedMessage() + "\n" + Utils.ExceptionTraceToString(exception)); 64 | } 65 | 66 | @Override 67 | protected void onCleanup() { 68 | if (jGitWrapper != null) 69 | jGitWrapper.cleanup(); 70 | } 71 | 72 | 73 | private ProgressMonitor monitor = new ProgressMonitor() { 74 | 75 | private int progress = 0; 76 | private int totalWork = 0; 77 | private int workCompleted = 0; 78 | 79 | public void start(int totalTasks) { 80 | } 81 | 82 | public void beginTask(String title, int totalWork) { 83 | this.totalWork = totalWork; 84 | this.workCompleted = 0; 85 | publishProgress(new SyncEvent(SyncEvent.State.SecondaryProgress, 0)); 86 | } 87 | 88 | public void update(int completed) { 89 | this.workCompleted += completed; 90 | int newProgress = getProgress(); 91 | 92 | if(this.progress != newProgress) { 93 | this.progress = newProgress; 94 | publishProgress(new SyncEvent(SyncEvent.State.SecondaryProgress, newProgress)); 95 | } 96 | } 97 | 98 | private int getProgress() { 99 | if(totalWork == 0) 100 | return 0; 101 | 102 | final int taskWorkProgress = (int) ((100.0 / totalWork) 103 | * workCompleted); 104 | return taskWorkProgress; 105 | } 106 | 107 | public void endTask() { 108 | } 109 | 110 | public boolean isCancelled() { 111 | return false; 112 | } 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/utils/SafeAsyncTask.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.utils; 2 | 3 | import android.content.Context; 4 | import android.os.AsyncTask; 5 | import android.util.Log; 6 | import android.widget.Toast; 7 | 8 | /** 9 | * Wrapper for {@link android.os.AsyncTask} that allows exceptions to be caught and presented to the 10 | * user in a meaningful way. 11 | *

12 | * It is required to override {@link #safeDoInBackground(Object[])}, which will be executed within 13 | * {@link android.os.AsyncTask#execute(Object[])}. If an exception is caught it will be sent to 14 | * {@link #onCancelled(Object)}, which will extract a meaningful message from an exception. The 15 | * message will be reported to the user with {@link #reportError(String)}, which will either display a toast, log the error 16 | * message to Logcat or do nothing. The reporting behaviour is controlled by {@link #mode}. 17 | *

18 | * When an exception is caught {@link #onError()} will be called, otherwise {@link 19 | * #onSuccess(Object)} is called. Finally {@link #onCleanup()} will always be called. 20 | *

21 | */ 22 | public abstract class SafeAsyncTask extends AsyncTask { 23 | public enum ReportMode { 24 | Toast, Log, Silent 25 | } 26 | 27 | final private ReportMode mode; 28 | final protected Context context; 29 | 30 | protected Exception exception = null; 31 | 32 | /** 33 | * Main worker method. It is run within a {@link android.os.AsyncTask#doInBackground(Object[])}. 34 | * Any exception this method throws is caught and reported according to the given {@link 35 | * #mode}. 36 | */ 37 | abstract protected Result safeDoInBackground(Params... params) throws Exception; 38 | 39 | /** Run after {@link #safeDoInBackground(Object[])} doesn't throw exception. */ 40 | protected void onSuccess(Result result) {} 41 | 42 | /** Run after {@link #safeDoInBackground(Object[])} throws an exception. */ 43 | protected void onError() {} 44 | 45 | /** Guaranteed to run after either {@link #onSuccess(Object)} or {@link #onError()} is run. */ 46 | protected void onCleanup() {} 47 | 48 | public SafeAsyncTask(Context context, ReportMode mode) { 49 | this.context = context; 50 | this.mode = mode; 51 | } 52 | 53 | @Override 54 | final protected Result doInBackground(Params... params) { 55 | try { 56 | return safeDoInBackground(params); 57 | } 58 | catch (Exception e) { 59 | exception = e; 60 | cancel(true); 61 | return null; 62 | } 63 | } 64 | 65 | @Override 66 | protected void onPostExecute(Result result) { 67 | super.onPostExecute(result); 68 | onSuccess(result); 69 | onCleanup(); 70 | } 71 | 72 | 73 | @Override 74 | final protected void onCancelled() { 75 | if (exception != null) { 76 | if (exception instanceof ReportableException) 77 | reportError(exception.getLocalizedMessage()); 78 | else 79 | reportError(exception.getLocalizedMessage()); 80 | Log.e("SafeAsyncTask", "safeDoInBackground() threw exception", exception); 81 | } 82 | 83 | onError(); 84 | onCleanup(); 85 | } 86 | 87 | @Override 88 | final protected void onCancelled(Result result) { 89 | onCancelled(); 90 | } 91 | 92 | /** Reports the error according to {@link #mode}. */ 93 | protected void reportError(String error) { 94 | if (error == null || error.isEmpty()) 95 | return; 96 | 97 | if (mode == ReportMode.Toast && context != null) 98 | Toast.makeText(context, error, Toast.LENGTH_LONG).show(); 99 | if (mode != ReportMode.Silent) 100 | Log.d("SafeAsyncTask", error); 101 | } 102 | 103 | 104 | public static class ReportableException extends Exception { 105 | public ReportableException(String message) { 106 | super(message); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/data/dao/OrgNodeRepository.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.data.dao; 2 | 3 | import com.j256.ormlite.stmt.DeleteBuilder; 4 | import com.j256.ormlite.stmt.QueryBuilder; 5 | import com.j256.ormlite.stmt.Where; 6 | 7 | import java.sql.SQLException; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class OrgNodeRepository { 13 | 14 | public static OrgNode queryForId(Integer id) { 15 | return DatabaseHelper.getOrgNodeDao().queryForId(id); 16 | } 17 | 18 | public static void update(OrgNode node) { 19 | if (node.state != OrgNode.State.Added) // Keep the added state when updating nodes 20 | node.state = OrgNode.State.Updated; 21 | 22 | DatabaseHelper.getOrgNodeDao().update(node); 23 | } 24 | 25 | public static void create(OrgNode node) { 26 | if (node.type != OrgNode.Type.Directory) // Directories should always have state=clean 27 | node.state = OrgNode.State.Added; 28 | 29 | DatabaseHelper.getOrgNodeDao().create(node); 30 | } 31 | 32 | public static void delete(OrgNode node) { 33 | if (node.type == OrgNode.Type.File || node.type == OrgNode.Type.Directory) 34 | deleteWithoutUpdate(node); 35 | else 36 | deleteWithUpdate(node); 37 | } 38 | 39 | private static void deleteWithUpdate(OrgNode node) { 40 | node.state = OrgNode.State.Deleted; 41 | DatabaseHelper.getOrgNodeDao().update(node); 42 | 43 | for(OrgNode child: node.children) 44 | deleteWithUpdate(child); 45 | } 46 | 47 | private static void deleteWithoutUpdate(OrgNode node) { 48 | for(OrgNode child: node.children) 49 | deleteWithoutUpdate(child); 50 | 51 | DatabaseHelper.getOrgNodeDao().delete(node); 52 | } 53 | 54 | public static QueryBuilder queryBuilder() { 55 | return DatabaseHelper.getOrgNodeDao().queryBuilder(); 56 | } 57 | 58 | public static DeleteBuilder deleteBuilder() { 59 | return DatabaseHelper.getOrgNodeDao().deleteBuilder(); 60 | } 61 | 62 | 63 | public static void deleteAll() { 64 | try { 65 | deleteBuilder().delete(); 66 | } catch (SQLException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | public static int delete(OrgFile orgFile) throws SQLException { 72 | DeleteBuilder deleteBuilder = deleteBuilder(); 73 | deleteBuilder.where().eq(OrgNode.FILE_FIELD_NAME, orgFile); 74 | return deleteBuilder.delete(); 75 | } 76 | 77 | public static List getRootNodes() { 78 | try { 79 | List children = queryBuilder().where().isNull(OrgNode.PARENT_FIELD_NAME).and().ne(OrgNode.STATE_FIELD_NAME, OrgNode.State.Deleted).query(); 80 | Collections.sort(children, new OrgNode.OrgNodeCompare()); 81 | return children; 82 | } catch (SQLException e) { 83 | e.printStackTrace(); 84 | } 85 | 86 | return new ArrayList(); 87 | } 88 | 89 | public static List getScheduledNodes(String filename, boolean showHabits) { 90 | try { 91 | Where query = queryBuilder().where().eq(OrgNode.FILE_FIELD_NAME, filename); 92 | query.and().like(OrgNode.TITLE_FIELD_NAME, "%<%>%"); 93 | 94 | if (showHabits == false) 95 | query.and().not().like(OrgNode.TITLE_FIELD_NAME, "%:STYLE: habit%"); 96 | 97 | return query.query(); 98 | } catch (SQLException e) { 99 | e.printStackTrace(); 100 | } 101 | 102 | return new ArrayList(); 103 | } 104 | 105 | public static List getDirtyNodes(OrgFile file) throws SQLException { 106 | Where builder = queryBuilder().orderBy(OrgNode.LINENUMBER_FIELD_NAME, false).where(); 107 | builder.eq(OrgNode.FILE_FIELD_NAME, file); 108 | builder.and().ne(OrgNode.STATE_FIELD_NAME, OrgNode.State.Clean); 109 | return builder.query(); 110 | } 111 | 112 | public static OrgNode getDefaultCaptureNode() { 113 | return new OrgNode(); // TODO 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/settings/KeySettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.settings; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.os.Environment; 8 | import android.preference.PreferenceManager; 9 | import android.text.TextUtils; 10 | import android.widget.Toast; 11 | 12 | import com.hdweiss.morgand.R; 13 | import com.jcraft.jsch.JSch; 14 | import com.jcraft.jsch.KeyPair; 15 | import com.lamerman.FileDialog; 16 | import com.lamerman.SelectionMode; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.FileOutputStream; 20 | import java.io.IOException; 21 | 22 | public class KeySettingActivity extends Activity { 23 | 24 | public final static String KeyfileName = "keyfile"; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | 30 | Intent intent = new Intent(getBaseContext(), FileDialog.class); 31 | intent.putExtra(FileDialog.START_PATH, Environment.getExternalStorageDirectory().getPath()); 32 | intent.putExtra(FileDialog.CAN_SELECT_DIR, false); 33 | //intent.putExtra(FileDialog.FORMAT_FILTER, new String[] { "_rsa" }); 34 | intent.putExtra(FileDialog.SELECTION_MODE, SelectionMode.MODE_OPEN); 35 | 36 | startActivityForResult(intent, 0); 37 | } 38 | 39 | @Override 40 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 41 | if (resultCode == Activity.RESULT_OK) { 42 | String filePath = data.getStringExtra(FileDialog.RESULT_PATH); 43 | 44 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 45 | String passphrase = prefs.getString("git_password", ""); 46 | if (CopyKeyToStorage(filePath, passphrase)) { 47 | SharedPreferences.Editor edit = prefs.edit(); 48 | edit.putString("git_key_info", GetKeyprint(GetInternalKeyPath(), passphrase)); 49 | edit.putString("git_key_path", GetInternalKeyPath()); 50 | edit.commit(); 51 | } 52 | } else if (resultCode == Activity.RESULT_CANCELED) { 53 | } 54 | finish(); 55 | } 56 | 57 | private String GetKeyprint(String keyfilePath, String passphrase) { 58 | try { 59 | KeyPair keyPair = KeyPair.load(new JSch(), keyfilePath); 60 | if (!passphrase.isEmpty() && keyPair.isEncrypted()) 61 | keyPair.decrypt(passphrase); 62 | else if (passphrase.isEmpty() && keyPair.isEncrypted()) { 63 | Toast.makeText(this, R.string.error_key_need_pass, Toast.LENGTH_LONG).show(); 64 | return ""; 65 | } 66 | String fingerprint = keyPair.getFingerPrint(); 67 | keyPair.dispose(); 68 | return fingerprint; 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | return ""; 73 | } 74 | 75 | private boolean CopyKeyToStorage(String filePath, String passphrase) { 76 | String fingerprint = GetKeyprint(filePath, passphrase); 77 | if (TextUtils.isEmpty(fingerprint)) { 78 | Toast.makeText(this, R.string.error_key_file_info, Toast.LENGTH_LONG).show(); 79 | return false; 80 | } 81 | 82 | try { 83 | FileInputStream fis = new FileInputStream(filePath); 84 | FileOutputStream fos = openFileOutput(KeyfileName, MODE_PRIVATE); 85 | 86 | byte[] buffer = new byte[1444]; 87 | int byteread = 0; 88 | int bytesum = 0; 89 | while ((byteread = fis.read(buffer)) != -1) { 90 | bytesum += byteread; 91 | fos.write(buffer, 0, byteread); 92 | } 93 | fos.close(); 94 | fis.close(); 95 | return true; 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | Toast.makeText(this, getText(R.string.error_key_file_copy), Toast.LENGTH_LONG); 99 | } 100 | 101 | return false; 102 | } 103 | 104 | private String GetInternalKeyPath() { 105 | return getFilesDir() + "/" + KeyfileName; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/SynchronizerNotification.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Notification; 5 | import android.app.NotificationManager; 6 | import android.app.PendingIntent; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.os.Build; 10 | 11 | import com.hdweiss.morgand.R; 12 | 13 | public class SynchronizerNotification { 14 | private NotificationManager notificationManager; 15 | private Notification notification; 16 | private int notifyRef = 1; 17 | private Context context; 18 | 19 | public SynchronizerNotification(Context context) { 20 | this.context = context; 21 | } 22 | 23 | public void errorNotification(String errorMsg) { 24 | this.notificationManager = (NotificationManager) context 25 | .getSystemService(Context.NOTIFICATION_SERVICE); 26 | Intent notifyIntent = new Intent(context, MainActivity.class); 27 | notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP 28 | | Intent.FLAG_ACTIVITY_SINGLE_TOP); 29 | 30 | PendingIntent contentIntent = PendingIntent.getActivity(context, 0, 31 | notifyIntent, 0); 32 | 33 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 34 | notification = getBigstyleNotification(errorMsg, contentIntent); 35 | } else { 36 | notification = getSimpleNotification(errorMsg, contentIntent); 37 | } 38 | notificationManager.notify(notifyRef, notification); 39 | } 40 | 41 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 42 | private Notification getBigstyleNotification(String message, PendingIntent contentIntent) { 43 | Notification notification = new Notification.BigTextStyle( 44 | new Notification.Builder(context) 45 | .setContentIntent(contentIntent) 46 | .setContentTitle(context.getString(R.string.error_sync)) 47 | .setContentText("Error") 48 | .setSmallIcon(R.drawable.ic_launcher)) 49 | .bigText(message) 50 | .build(); 51 | return notification; 52 | } 53 | 54 | 55 | private Notification getSimpleNotification(String message, PendingIntent contentIntent) { 56 | Notification.Builder builder = new Notification.Builder(context); 57 | builder.setContentIntent(contentIntent); 58 | builder.setSmallIcon(R.drawable.ic_launcher); 59 | builder.setContentText(message); 60 | return builder.getNotification(); 61 | } 62 | 63 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 64 | public void setupNotification() { 65 | this.notificationManager = (NotificationManager) context 66 | .getSystemService(Context.NOTIFICATION_SERVICE); 67 | Intent notifyIntent = new Intent(context, MainActivity.class); 68 | notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP 69 | | Intent.FLAG_ACTIVITY_SINGLE_TOP); 70 | 71 | PendingIntent contentIntent = PendingIntent.getActivity(context, 0, 72 | notifyIntent, 0); 73 | 74 | Notification.Builder builder = new Notification.Builder(context); 75 | builder.setContentIntent(contentIntent); 76 | builder.setSmallIcon(R.drawable.ic_launcher); 77 | builder.setOngoing(true); 78 | builder.setContentTitle(context.getString(R.string.title_synchronizing_changes)); 79 | builder.setProgress(100, 0, true); 80 | notification = builder.getNotification(); 81 | 82 | notificationManager.notify(notifyRef, notification); 83 | } 84 | 85 | public void updateNotification(String message) { 86 | if(notification == null) 87 | return; 88 | 89 | if(message != null) { 90 | notificationManager.notify(notifyRef, notification); 91 | } 92 | } 93 | 94 | public void updateNotification(int progress) { 95 | updateNotification(progress, null); 96 | } 97 | 98 | public void updateNotification(int progress, String message) { 99 | if(notification == null) 100 | return; 101 | 102 | notification.contentView.setProgressBar(android.R.id.progress, 100, 103 | progress, false); 104 | 105 | notificationManager.notify(notifyRef, notification); 106 | } 107 | 108 | public void finalizeNotification() { 109 | notificationManager.cancel(notifyRef); 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/SyncService.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.app.Service; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.SharedPreferences; 9 | import android.os.AsyncTask; 10 | import android.os.IBinder; 11 | import android.preference.PreferenceManager; 12 | 13 | import com.hdweiss.morgand.Application; 14 | import com.hdweiss.morgand.synchronizer.writer.SyncWriterTask; 15 | 16 | public class SyncService extends Service implements 17 | SharedPreferences.OnSharedPreferenceChangeListener { 18 | private static final String ACTION = "action"; 19 | private static final String START_ALARM = "START_ALARM"; 20 | private static final String STOP_ALARM = "STOP_ALARM"; 21 | 22 | private SharedPreferences appSettings; 23 | 24 | private AlarmManager alarmManager; 25 | private PendingIntent alarmIntent; 26 | private boolean alarmScheduled = false; 27 | private AsyncTask syncTask; 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | this.appSettings = PreferenceManager 33 | .getDefaultSharedPreferences(getApplicationContext()); 34 | this.appSettings.registerOnSharedPreferenceChangeListener(this); 35 | this.alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 36 | } 37 | 38 | @Override 39 | public void onDestroy() { 40 | unsetAlarm(); 41 | this.appSettings.unregisterOnSharedPreferenceChangeListener(this); 42 | super.onDestroy(); 43 | } 44 | 45 | public static void stopAlarm(Context context) { 46 | Intent intent = new Intent(context, SyncService.class); 47 | intent.putExtra(ACTION, SyncService.STOP_ALARM); 48 | context.startService(intent); 49 | } 50 | 51 | public static void startAlarm(Context context) { 52 | Intent intent = new Intent(context, SyncService.class); 53 | intent.putExtra(ACTION, SyncService.START_ALARM); 54 | context.startService(intent); 55 | } 56 | 57 | @Override 58 | public int onStartCommand(Intent intent, int flags, int startId) { 59 | if (intent == null) 60 | return 0; 61 | String action = intent.getStringExtra(ACTION); 62 | if (action != null && action.equals(START_ALARM)) 63 | setAlarm(); 64 | else if (action != null && action.equals(STOP_ALARM)) 65 | unsetAlarm(); 66 | else if (syncTask == null || syncTask.getStatus() == AsyncTask.Status.FINISHED) 67 | runSynchronizerAsync(); 68 | return 0; 69 | } 70 | 71 | private void runSynchronizerAsync() { 72 | unsetAlarm(); 73 | syncTask = new SyncWriterTask(getBaseContext()).execute(); 74 | setAlarm(); 75 | } 76 | 77 | 78 | private void setAlarm() { 79 | boolean doAutoSync = this.appSettings.getBoolean("sync_auto", false); 80 | if (!this.alarmScheduled && doAutoSync) { 81 | 82 | int interval = Integer.parseInt( 83 | this.appSettings.getString("sync_frequency", "1800000"), 84 | 10); 85 | 86 | this.alarmIntent = PendingIntent.getService(Application.getInstace(), 0, new Intent( 87 | this, SyncService.class), 0); 88 | alarmManager.setInexactRepeating(AlarmManager.RTC, 89 | System.currentTimeMillis() + interval, interval, 90 | alarmIntent); 91 | 92 | this.alarmScheduled = true; 93 | } 94 | } 95 | 96 | private void unsetAlarm() { 97 | if (this.alarmScheduled) { 98 | this.alarmManager.cancel(this.alarmIntent); 99 | this.alarmScheduled = false; 100 | } 101 | } 102 | 103 | private void resetAlarm() { 104 | unsetAlarm(); 105 | setAlarm(); 106 | } 107 | 108 | @Override 109 | public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { 110 | if (key.equals("sync_auto")) { 111 | boolean syncAuto = preferences.getBoolean("sync_auto", false); 112 | if (syncAuto && !this.alarmScheduled) 113 | setAlarm(); 114 | else if (syncAuto == false && this.alarmScheduled) 115 | unsetAlarm(); 116 | } else if (key.equals("sync_frequency")) 117 | resetAlarm(); 118 | } 119 | 120 | @Override 121 | public IBinder onBind(Intent intent) { 122 | return null; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/outline/OutlineFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.outline; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.Menu; 7 | import android.view.MenuInflater; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.hdweiss.morgand.Application; 13 | import com.hdweiss.morgand.R; 14 | import com.hdweiss.morgand.data.dao.OrgNode; 15 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 16 | import com.hdweiss.morgand.events.DataUpdatedEvent; 17 | import com.hdweiss.morgand.gui.edit.BaseEditFragment; 18 | import com.j256.ormlite.android.apptools.OpenHelperManager; 19 | import com.squareup.otto.Subscribe; 20 | 21 | public class OutlineFragment extends Fragment { 22 | 23 | protected OutlineListView listView; 24 | 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | Application.getBus().register(this); 29 | setHasOptionsMenu(true); 30 | } 31 | 32 | @Override 33 | public void onDestroy() { 34 | Application.getBus().unregister(this); 35 | super.onDestroy(); 36 | OpenHelperManager.releaseHelper(); 37 | } 38 | 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 41 | Bundle savedInstanceState) { 42 | View rootView = inflater.inflate(R.layout.fragment_outline, container, false); 43 | listView = (OutlineListView) rootView.findViewById(R.id.list); 44 | listView.setEmptyView(rootView.findViewById(R.id.outline_list_empty)); 45 | return rootView; 46 | } 47 | 48 | 49 | @Override 50 | public void onActivityCreated(Bundle savedInstanceState) { 51 | super.onActivityCreated(savedInstanceState); 52 | 53 | listView.setActivity(getActivity()); 54 | refreshView(); 55 | } 56 | 57 | 58 | @Override 59 | public void onViewStateRestored(Bundle savedInstanceState) { 60 | super.onViewStateRestored(savedInstanceState); 61 | 62 | if (savedInstanceState == null) 63 | return; 64 | 65 | if (listView != null) 66 | listView.loadState(savedInstanceState); 67 | } 68 | 69 | @Override 70 | public void onSaveInstanceState(Bundle outState) { 71 | super.onSaveInstanceState(outState); 72 | 73 | if (listView != null) 74 | listView.saveState(outState); 75 | } 76 | 77 | @Override 78 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 79 | inflater.inflate(R.menu.outline, menu); 80 | } 81 | 82 | @Override 83 | public void onPrepareOptionsMenu(Menu menu) { 84 | super.onPrepareOptionsMenu(menu); 85 | 86 | if (listView == null || listView.getAdapter().isEmpty()) { 87 | menu.findItem(R.id.delete).setEnabled(false); 88 | menu.findItem(R.id.add_child).setEnabled(false); 89 | } else { 90 | menu.findItem(R.id.delete).setEnabled(true); 91 | menu.findItem(R.id.add_child).setEnabled(true); 92 | } 93 | } 94 | 95 | @Override 96 | public boolean onOptionsItemSelected(MenuItem item) { 97 | int position = listView.getCheckedItemPosition(); 98 | switch(item.getItemId()) { 99 | case android.R.id.home: 100 | if (listView != null) 101 | listView.collapseCurrent(); 102 | break; 103 | 104 | case R.id.add_child: 105 | showAddDialog(position); 106 | break; 107 | 108 | case R.id.delete: 109 | showDeleteDialog(position); 110 | break; 111 | 112 | default: 113 | return super.onOptionsItemSelected(item); 114 | } 115 | 116 | return true; 117 | } 118 | 119 | private void showDeleteDialog(int position) { 120 | if (position < 0) 121 | return; 122 | 123 | OrgNode node = (OrgNode) listView.getAdapter().getItem(position); 124 | OrgNodeRepository.delete(node); 125 | Application.getBus().post(new DataUpdatedEvent()); 126 | } 127 | 128 | private void showAddDialog(int position) { 129 | if (position < 0) 130 | return; 131 | 132 | OrgNode node = (OrgNode) listView.getAdapter().getItem(position); 133 | BaseEditFragment fragment = BaseEditFragment.getAddFragment(node); 134 | fragment.show(getActivity()); 135 | } 136 | 137 | 138 | @Subscribe 139 | public void refreshView(DataUpdatedEvent event) { 140 | refreshView(); 141 | } 142 | protected void refreshView() { 143 | listView.refresh(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/layout/fragment_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 21 | 22 | 31 | 32 | 40 | 41 | 47 | 48 | 53 | 54 | 60 | 61 | 69 | 70 | 77 | 78 | 79 | 80 | 86 | 87 | 95 | 96 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/parser/OrgRepository.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.parser; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.hdweiss.morgand.data.dao.OrgFile; 6 | import com.hdweiss.morgand.data.dao.OrgNode; 7 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 8 | import com.j256.ormlite.dao.RuntimeExceptionDao; 9 | import com.j256.ormlite.stmt.Where; 10 | 11 | import java.io.File; 12 | import java.io.FileFilter; 13 | import java.sql.SQLException; 14 | import java.util.ArrayList; 15 | 16 | public class OrgRepository { 17 | 18 | private String path; 19 | private OrgNodeRepository nodeDao; 20 | private RuntimeExceptionDao fileDao; 21 | 22 | public OrgRepository(String path) { 23 | if (TextUtils.isEmpty(path)) 24 | throw new IllegalArgumentException("Path can't be empty"); 25 | 26 | this.path = path; 27 | this.nodeDao = new OrgNodeRepository(); 28 | this.fileDao = OrgFile.getDao(); 29 | } 30 | 31 | /** 32 | * @return List of OrgFiles that have been modified. Both file and file.node need to be updated/created. 33 | */ 34 | public ArrayList getModifiedFiles() { 35 | File rootFolder = new File(path); 36 | 37 | if (rootFolder.exists() == false) 38 | throw new IllegalArgumentException("Folder " + path + " does not exist"); 39 | 40 | if (rootFolder.canRead() == false) 41 | throw new IllegalArgumentException("Can't parse " + path); 42 | 43 | ArrayList modifiedOrgFiles = new ArrayList(); 44 | getModifiedFiles(rootFolder, null, modifiedOrgFiles); 45 | return modifiedOrgFiles; 46 | } 47 | 48 | private void getModifiedFiles(File parentFile, OrgNode parent, ArrayList modifiedOrgFiles) { 49 | for (File file : parentFile.listFiles()) { 50 | if (file.isDirectory() && file.isHidden() == false && hasOrgFiles(file)) { 51 | OrgNode directoryNode = findOrCreateDirectoryNode(file, parent); 52 | getModifiedFiles(file, directoryNode, modifiedOrgFiles); 53 | } else if (file.isFile() && file.getName().endsWith(".org")) { 54 | OrgFile orgFile = getOrCreateOrgFile(file, parent); 55 | if (orgFile != null) 56 | modifiedOrgFiles.add(orgFile); 57 | } 58 | } 59 | } 60 | 61 | 62 | private boolean hasOrgFiles(File file) { 63 | if (file.listFiles() == null) 64 | return false; 65 | 66 | for(File subFile: file.listFiles()) { 67 | if (file.isDirectory()) { 68 | if (hasOrgFiles(subFile)) 69 | return true; 70 | } 71 | } 72 | 73 | File[] files = file.listFiles(new FileFilter() { 74 | @Override 75 | public boolean accept(File file) { 76 | return file.isFile() && file.getName().endsWith(".org"); 77 | } 78 | }); 79 | 80 | return files.length > 0; 81 | } 82 | 83 | private OrgNode findOrCreateDirectoryNode(File file, OrgNode parent) { 84 | try { 85 | 86 | Where query = nodeDao.queryBuilder().where(); 87 | if (parent != null) 88 | query.eq(OrgNode.PARENT_FIELD_NAME, parent).and(); 89 | else 90 | query.isNull(OrgNode.PARENT_FIELD_NAME).and(); 91 | query.eq(OrgNode.TITLE_FIELD_NAME, file.getName()); 92 | OrgNode node = query.queryForFirst(); 93 | if (node != null) 94 | return node; 95 | } catch (SQLException e) { 96 | e.printStackTrace(); 97 | } 98 | 99 | OrgNode node = new OrgNode(); 100 | node.type = OrgNode.Type.Directory; 101 | node.parent = parent; 102 | node.title = file.getName(); 103 | node.level = 0; 104 | nodeDao.create(node); 105 | return node; 106 | } 107 | 108 | private OrgFile getOrCreateOrgFile(File file, OrgNode parent) { 109 | OrgFile orgFile = fileDao.queryForId(file.getAbsolutePath()); 110 | 111 | if (orgFile == null) { 112 | orgFile = new OrgFile(); 113 | orgFile.path = file.getAbsolutePath(); 114 | } else { 115 | if (file.lastModified() <= orgFile.lastModified) 116 | return null; 117 | 118 | try { 119 | nodeDao.delete(orgFile); 120 | } catch (SQLException e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | 125 | orgFile.node = getRootNode(file, orgFile, parent); 126 | return orgFile; 127 | } 128 | 129 | private OrgNode getRootNode(File file, OrgFile orgFile, OrgNode parent) { 130 | OrgNode rootNode = new OrgNode(); 131 | rootNode.type = OrgNode.Type.File; 132 | rootNode.title = file.getName(); 133 | rootNode.file = orgFile; 134 | rootNode.parent = parent; 135 | rootNode.level = 0; 136 | return rootNode; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/res/values/strings_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | Settings 3 | 4 | General 5 | Advanced 6 | 7 | 8 | General 9 | Version 10 | 11 | 12 | Interface 13 | Outline 14 | 15 | Show drawers 16 | Show drawers in outline 17 | 18 | Show settings 19 | Show file settings in outline 20 | 21 | Allow subtree expansion 22 | Allow outline to mimic org-mode\'s visibility cycling of headlines 23 | 24 | 25 | 26 | Data & sync 27 | Sync frequency 28 | Background sync 29 | Automatically synchronize in background 30 | Sync mode 31 | Sync with Wifi only 32 | Only try to synchronize when Wireless is up 33 | 34 | Active todo keywords 35 | Inactive todo keywords 36 | Priorities 37 | 38 | 39 | 1 min 40 | 5 min 41 | 10 min 42 | 15 min 43 | 30 min 44 | 45 min 45 | 1 hour 46 | 3 hours 47 | 6 hours 48 | 12 hours 49 | 50 | 51 | 52 | 53 | Git 54 | Authentication 55 | Git Url 56 | Git branch 57 | Git commit author 58 | Git commit email 59 | Private ssh key file 60 | Git (or key) password 61 | Git Username 62 | Git local repository path 63 | Conflict merge strategy 64 | 65 | Prefer remote changes (theirs) 66 | Prefer local changes (ours) 67 | 68 | 69 | 70 | Calendar 71 | Display 72 | Clear phone calendar 73 | Clears the phones\' calendar of all entries inserted by MobileOrg 74 | Synchronize scheduled items with the system calendar 75 | Synchronize with calendar 76 | Choose calendar MobileOrg will use 77 | Calendar name 78 | Assimilate calendar entries not inserted by MobileOrg and add them to capture file. EXPERIMENTAL! 79 | Assimilate calendar entries 80 | Delete entries that have been assimilated from the calendar 81 | Delete on assimilation 82 | Clear phone calendar 83 | Are you sure you want to clear the phones\' calendar? 84 | Add reminders for scheduled items to calendar 85 | Reminders 86 | Sets the interval when an alarm is triggered 87 | Reminder interval (minutes) 88 | Shows habits in the calendar 89 | Show habits 90 | Show items with done TODO keywords the calendar 91 | Show done events 92 | Show past items in the calendar 93 | Show past events 94 | 95 | 96 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/edit/EditDateFragment.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.edit; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.CheckBox; 8 | import android.widget.CompoundButton; 9 | import android.widget.DatePicker; 10 | import android.widget.TimePicker; 11 | 12 | import com.hdweiss.morgand.R; 13 | import com.hdweiss.morgand.data.dao.OrgNode; 14 | 15 | import java.util.Calendar; 16 | 17 | public class EditDateFragment extends BaseEditFragment { 18 | 19 | private DatePicker datePicker; 20 | private CheckBox timeStartCheckbox; 21 | private TimePicker timeStartPicker; 22 | private CheckBox timeEndCheckbox; 23 | private TimePicker timeEndPicker; 24 | 25 | @Override 26 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 27 | View view = inflater.inflate(R.layout.edit_date_fragment, container, false); 28 | 29 | datePicker = (DatePicker) view.findViewById(R.id.date); 30 | datePicker.setCalendarViewShown(false); 31 | datePicker.setSpinnersShown(true); 32 | 33 | timeStartPicker = (TimePicker) view.findViewById(R.id.time_start); 34 | timeStartPicker.setIs24HourView(true); 35 | timeStartCheckbox = (CheckBox) view.findViewById(R.id.time_start_check); 36 | 37 | timeEndPicker = (TimePicker) view.findViewById(R.id.time_end); 38 | timeEndPicker.setIs24HourView(true); 39 | timeEndCheckbox = (CheckBox) view.findViewById(R.id.time_end_check); 40 | 41 | timeStartCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 42 | @Override 43 | public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 44 | if (checked) { 45 | timeStartPicker.setVisibility(View.VISIBLE); 46 | timeEndCheckbox.setEnabled(true); 47 | } 48 | else { 49 | timeStartPicker.setVisibility(View.INVISIBLE); 50 | timeEndCheckbox.setChecked(false); 51 | timeEndCheckbox.setEnabled(false); 52 | } 53 | } 54 | }); 55 | 56 | timeEndCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 57 | @Override 58 | public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 59 | if (checked) 60 | timeEndPicker.setVisibility(View.VISIBLE); 61 | else 62 | timeEndPicker.setVisibility(View.INVISIBLE); 63 | } 64 | }); 65 | 66 | return view; 67 | } 68 | 69 | @Override 70 | public void onViewCreated(View view, Bundle savedInstanceState) { 71 | super.onViewCreated(view, savedInstanceState); 72 | 73 | 74 | // OrgNodeTimeDate timeDate = null; // TODO Complete 75 | controller.getNode().getTitle(); 76 | // if (timeDate != null) 77 | // type = timeDate.type; 78 | // else 79 | // type = OrgNodeTimeDate.TYPE.Scheduled; 80 | 81 | // getDialog().setTitle(getResources().getString(R.string.action_set_time) + " - " + type.toString()); 82 | 83 | // if (timeDate != null) 84 | // populateView(timeDate); 85 | // else 86 | // populateView(); 87 | } 88 | 89 | private void populateView() { 90 | Calendar c = Calendar.getInstance(); 91 | datePicker.updateDate(c.get(Calendar.YEAR) - 1, c.get(Calendar.MONTH) - 1, c.get(Calendar.DAY_OF_MONTH) - 1); 92 | 93 | timeStartCheckbox.setChecked(false); 94 | timeStartPicker.setVisibility(View.INVISIBLE); 95 | 96 | timeEndCheckbox.setChecked(false); 97 | timeEndCheckbox.setEnabled(false); 98 | timeEndPicker.setVisibility(View.INVISIBLE); 99 | } 100 | 101 | // private void populateView(OrgNodeTimeDate timeDate) { 102 | // if (timeDate.year >= 0 && timeDate.monthOfYear >= 0 && timeDate.dayOfMonth >= 0) 103 | // datePicker.updateDate(timeDate.year - 1, timeDate.monthOfYear - 1, timeDate.dayOfMonth - 1); 104 | // 105 | // if (timeDate.startTimeOfDay >= 0 && timeDate.startMinute >= 0) { 106 | // timeStartPicker.setCurrentHour(timeDate.startTimeOfDay); 107 | // timeStartPicker.setCurrentMinute(timeDate.startMinute); 108 | // timeStartCheckbox.setChecked(true); 109 | // } else 110 | // timeStartCheckbox.setChecked(false); 111 | // 112 | // if (timeDate.endTimeOfDay >= 0 && timeDate.endMinute >= 0) { 113 | // timeEndPicker.setCurrentHour(timeDate.endTimeOfDay); 114 | // timeEndPicker.setCurrentMinute(timeDate.endMinute); 115 | // timeEndCheckbox.setChecked(true); 116 | // } else 117 | // timeEndCheckbox.setChecked(false); 118 | // } 119 | // 120 | // 121 | // private OrgNodeTimeDate getTimeDate() { 122 | // OrgNodeTimeDate resultTimeDate = new OrgNodeTimeDate(type); 123 | // 124 | // resultTimeDate.year = datePicker.getYear() + 1; 125 | // resultTimeDate.monthOfYear = datePicker.getMonth() + 1; 126 | // resultTimeDate.dayOfMonth = datePicker.getDayOfMonth() + 1; 127 | // 128 | // if (timeStartCheckbox.isChecked()) { 129 | // resultTimeDate.startTimeOfDay = timeStartPicker.getCurrentHour(); 130 | // resultTimeDate.startMinute = timeStartPicker.getCurrentMinute(); 131 | // 132 | // if (timeEndCheckbox.isChecked()) { 133 | // resultTimeDate.endTimeOfDay = timeEndPicker.getCurrentHour(); 134 | // resultTimeDate.endMinute = timeEndPicker.getCurrentMinute(); 135 | // } 136 | // } 137 | // 138 | // return resultTimeDate; 139 | // } 140 | 141 | @Override 142 | public OrgNode getEditedNode() { 143 | return controller.getNode(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui; 2 | 3 | import android.app.ActionBar; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.support.v4.view.ViewPager; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.Window; 13 | import android.widget.Toast; 14 | 15 | import com.hdweiss.morgand.Application; 16 | import com.hdweiss.morgand.R; 17 | import com.hdweiss.morgand.data.dao.OrgFile; 18 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 19 | import com.hdweiss.morgand.events.DataUpdatedEvent; 20 | import com.hdweiss.morgand.events.SyncEvent; 21 | import com.hdweiss.morgand.settings.PreferenceUtils; 22 | import com.hdweiss.morgand.settings.SettingsActivity; 23 | import com.hdweiss.morgand.synchronizer.calendar.CalendarWrapper; 24 | import com.hdweiss.morgand.synchronizer.writer.SyncWriterTask; 25 | import com.squareup.otto.Subscribe; 26 | 27 | public class MainActivity extends FragmentActivity implements ActionBar.TabListener { 28 | 29 | private MainPagerAdapter mSectionsPagerAdapter; 30 | private ViewPager mViewPager; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | 36 | requestWindowFeature(Window.FEATURE_PROGRESS); 37 | setContentView(R.layout.activity_main); 38 | ActionBar actionBar = initActionbar(); 39 | initViewPager(actionBar); 40 | } 41 | 42 | private ActionBar initActionbar() { 43 | final ActionBar actionBar = getActionBar(); 44 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 45 | //actionBar.setDisplayShowTitleEnabled(false); 46 | actionBar.setHomeButtonEnabled(true); 47 | //actionBar.setDisplayHomeAsUpEnabled(false); 48 | return actionBar; 49 | } 50 | 51 | private void initViewPager(final ActionBar actionBar) { 52 | mSectionsPagerAdapter = new MainPagerAdapter(getSupportFragmentManager()); 53 | mViewPager = (ViewPager) findViewById(R.id.pager); 54 | mViewPager.setAdapter(mSectionsPagerAdapter); 55 | 56 | mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 57 | @Override 58 | public void onPageSelected(int position) { 59 | actionBar.setSelectedNavigationItem(position); 60 | } 61 | }); 62 | 63 | for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { 64 | actionBar.addTab( 65 | actionBar.newTab() 66 | .setText(mSectionsPagerAdapter.getPageTitle(i)) 67 | .setTabListener(this)); 68 | } 69 | } 70 | 71 | 72 | @Override 73 | protected void onResume() { 74 | super.onResume(); 75 | Application.getBus().register(this); 76 | } 77 | 78 | @Override 79 | protected void onPause() { 80 | Application.getBus().unregister(this); 81 | super.onPause(); 82 | } 83 | 84 | 85 | @Override 86 | public void onTabSelected(ActionBar.Tab tab, android.app.FragmentTransaction fragmentTransaction) { 87 | mViewPager.setCurrentItem(tab.getPosition()); 88 | } 89 | 90 | @Override 91 | public void onTabUnselected(ActionBar.Tab tab, android.app.FragmentTransaction fragmentTransaction) { 92 | } 93 | 94 | @Override 95 | public void onTabReselected(ActionBar.Tab tab, android.app.FragmentTransaction fragmentTransaction) { 96 | } 97 | 98 | 99 | @Override 100 | public boolean onCreateOptionsMenu(Menu menu) { 101 | getMenuInflater().inflate(R.menu.main, menu); 102 | return true; 103 | } 104 | 105 | @Override 106 | public boolean onOptionsItemSelected(MenuItem item) { 107 | switch (item.getItemId()) { 108 | case R.id.action_settings: 109 | Intent intent = new Intent(this, SettingsActivity.class); 110 | startActivity(intent); 111 | break; 112 | 113 | case R.id.action_sync: 114 | SyncWriterTask synchronizerTask = new SyncWriterTask(this); 115 | synchronizerTask.execute(); 116 | break; 117 | 118 | case R.id.action_clearDB: 119 | OrgNodeRepository.deleteAll(); 120 | OrgFile.deleteAll(); 121 | Application.getBus().post(new DataUpdatedEvent()); 122 | new CalendarWrapper(this).deleteEntries(); 123 | break; 124 | } 125 | return super.onOptionsItemSelected(item); 126 | } 127 | 128 | 129 | @Subscribe 130 | public void updateSyncProgress(SyncEvent event) { 131 | if (event.state == SyncEvent.State.Done) { 132 | setProgress(Window.PROGRESS_END); 133 | return; 134 | } 135 | 136 | switch (event.state) { 137 | case Intermediate: 138 | setProgressBarVisibility(true); 139 | setProgressBarIndeterminate(true); 140 | break; 141 | 142 | case Progress: 143 | setProgressBarIndeterminate(false); 144 | int progress = (Window.PROGRESS_END - Window.PROGRESS_START) / 100 * event.progress; 145 | setProgress(progress); 146 | break; 147 | 148 | case SecondaryProgress: 149 | setProgressBarIndeterminate(false); 150 | int secondaryProgress = (Window.PROGRESS_END - Window.PROGRESS_START) / 100 * event.progress; 151 | setSecondaryProgress(secondaryProgress); 152 | break; 153 | } 154 | } 155 | 156 | public void runShowWiki(View view) { 157 | Intent intent = new Intent(Intent.ACTION_VIEW, 158 | Uri.parse("https://github.com/hdweiss/mOrgAnd.wiki")); 159 | startActivity(intent); 160 | } 161 | 162 | public void runDownloadWiki(View view) { 163 | Toast.makeText(this, R.string.action_downloadwiki, Toast.LENGTH_SHORT).show(); 164 | PreferenceUtils.setupGitToWiki(); 165 | new SyncWriterTask(this).execute(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/synchronizer/calendar/SyncCalendarTask.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.synchronizer.calendar; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.database.Cursor; 6 | import android.preference.PreferenceManager; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | 10 | import com.hdweiss.morgand.Application; 11 | import com.hdweiss.morgand.data.OrgCalendarEntry; 12 | import com.hdweiss.morgand.data.dao.OrgNode; 13 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 14 | import com.hdweiss.morgand.events.DataUpdatedEvent; 15 | import com.hdweiss.morgand.settings.PreferenceUtils; 16 | import com.hdweiss.morgand.utils.MultiMap; 17 | import com.hdweiss.morgand.utils.SafeAsyncTask; 18 | 19 | import java.util.HashSet; 20 | import java.util.List; 21 | 22 | public class SyncCalendarTask extends SafeAsyncTask { 23 | 24 | private CalendarWrapper calendarWrapper; 25 | 26 | private HashSet inactiveTodoKeywords = new HashSet(); 27 | private boolean showDone = true; 28 | private boolean showPast = true; 29 | private boolean showHabits = true; 30 | private boolean pullEnabled = false; 31 | private boolean pullDelete = false; 32 | 33 | private int inserted = 0; 34 | private int deleted = 0; 35 | private int unchanged = 0; 36 | 37 | public SyncCalendarTask(Context context) { 38 | super(context, ReportMode.Log); 39 | 40 | this.calendarWrapper = new CalendarWrapper(context); 41 | refreshPreferences(); 42 | } 43 | 44 | @Override 45 | protected Void safeDoInBackground(String... files) throws Exception { 46 | Log.d("Calendar", "Started synchronization"); 47 | if (files.length == 0) { 48 | Log.d("Calendar", "No modified files"); 49 | } 50 | 51 | for(String file: files) 52 | syncFileSchedule(file); 53 | 54 | if (pullEnabled) // TODO complete assimilate Calendar (implement function that delivers default capture node) 55 | ;//assimilateCalendar(); 56 | 57 | Log.d("Calendar", "Ended synchronization"); 58 | return null; 59 | } 60 | 61 | @Override 62 | protected void onProgressUpdate(DataUpdatedEvent... events) { 63 | for(DataUpdatedEvent event: events) 64 | Application.getBus().post(event); 65 | } 66 | 67 | private void syncFileSchedule(String filename) { 68 | inserted = 0; 69 | deleted = 0; 70 | unchanged = 0; 71 | 72 | MultiMap entries = getCalendarEntries(filename); 73 | List nodes = OrgNodeRepository.getScheduledNodes(filename, showHabits); 74 | 75 | Log.d("Calendar", filename + " has " + nodes.size() + " calendar entries"); 76 | 77 | for(OrgNode node: nodes) { 78 | for(OrgCalendarEntry date: node.getOrgNodeDates()) { 79 | if (shouldInsertEntry(date, node)) 80 | createOrUpdateEntry(entries, date, filename, node); 81 | } 82 | } 83 | 84 | removeCalendarEntries(entries); 85 | 86 | Log.d("Calendar", "Calendar (" + filename + ") Inserted: " + inserted 87 | + " and deleted: " + deleted + ", unchanged: " + unchanged); 88 | } 89 | 90 | private void createOrUpdateEntry(MultiMap entries, 91 | OrgCalendarEntry date, String filename, OrgNode node) { 92 | CalendarEntry insertedEntry = entries.findValue(date.beginTime, date); 93 | 94 | if (insertedEntry != null && insertedEntry.title.equals(date.getCalendarTitle())) { 95 | entries.remove(date.beginTime, insertedEntry); 96 | unchanged++; 97 | } else { 98 | calendarWrapper.insertEntry(date, node.getBody(), filename, 99 | node.getProperty("LOCATION"), node.getProperty("BUSY")); 100 | inserted++; 101 | } 102 | } 103 | 104 | private boolean shouldInsertEntry(OrgCalendarEntry date, OrgNode node) { 105 | String todo = node.getTodo(); 106 | if (this.showDone == false && TextUtils.isEmpty(todo) == false && inactiveTodoKeywords.contains(todo)) 107 | return false; 108 | 109 | if (this.showPast == false && date.isInPast()) 110 | return false; 111 | 112 | if (this.showHabits == false && "habit".equals(node.getProperty("STYLE"))) 113 | return false; 114 | 115 | return true; 116 | } 117 | 118 | private MultiMap getCalendarEntries(String filename) { 119 | Cursor query = calendarWrapper.getCalendarCursor(filename); 120 | CalendarEntriesParser entriesParser = new CalendarEntriesParser(query); 121 | 122 | MultiMap map = new MultiMap(); 123 | while (query.isAfterLast() == false) { 124 | CalendarEntry entry = entriesParser.getEntryFromCursor(query); 125 | map.put(entry.dtStart, entry); 126 | 127 | query.moveToNext(); 128 | } 129 | 130 | query.close(); 131 | return map; 132 | } 133 | 134 | private void removeCalendarEntries(MultiMap entries) { 135 | for (Long entryKey : entries.keySet()) { 136 | for (CalendarEntry entry : entries.get(entryKey)) { 137 | calendarWrapper.deleteEntry(entry); 138 | deleted++; 139 | } 140 | } 141 | } 142 | 143 | private void assimilateCalendar() { 144 | Cursor query = calendarWrapper.getUnassimilatedCalendarCursor(); 145 | 146 | CalendarEntriesParser entriesParser = new CalendarEntriesParser(query); 147 | 148 | while(query.isAfterLast() == false) { 149 | CalendarEntry entry = entriesParser.getEntryFromCursor(query); 150 | 151 | OrgNode captureNode = OrgNodeRepository.getDefaultCaptureNode(); 152 | entry.writeToOrgNodes(captureNode); 153 | 154 | if (this.pullDelete) 155 | calendarWrapper.deleteEntry(entry); 156 | 157 | query.moveToNext(); 158 | } 159 | 160 | query.close(); 161 | publishProgress(new DataUpdatedEvent()); 162 | } 163 | 164 | private void refreshPreferences() { 165 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 166 | this.pullEnabled = sharedPreferences.getBoolean("calendar_pull", false); 167 | this.pullDelete = sharedPreferences.getBoolean("calendar_pull_delete", false); 168 | this.showDone = sharedPreferences.getBoolean("calendar_show_done", true); 169 | this.showPast = sharedPreferences.getBoolean("calendar_show_past", true); 170 | this.showHabits = sharedPreferences.getBoolean("calendar_habits", true); 171 | this.inactiveTodoKeywords = PreferenceUtils.getInactiveTodoKeywords(); 172 | this.calendarWrapper.refreshPreferences(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/outline/OutlineListView.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.outline; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.preference.PreferenceManager; 7 | import android.util.AttributeSet; 8 | import android.view.ActionMode; 9 | import android.view.View; 10 | import android.widget.AdapterView; 11 | import android.widget.ListView; 12 | 13 | import com.hdweiss.morgand.data.OrgNodeUtils; 14 | import com.hdweiss.morgand.data.dao.OrgNode; 15 | import com.hdweiss.morgand.gui.edit.BaseEditFragment; 16 | import com.hdweiss.morgand.settings.PreferenceUtils; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | public class OutlineListView extends ListView { 22 | 23 | private final static String OUTLINE_NODES = "nodes"; 24 | private final static String OUTLINE_LEVELS = "levels"; 25 | private final static String OUTLINE_EXPANDED = "expanded"; 26 | private final static String OUTLINE_CHECKED_POS = "selection"; 27 | private final static String OUTLINE_SCROLL_POS = "scrollPosition"; 28 | 29 | private Context context; 30 | private Activity activity; 31 | 32 | private OutlineAdapter adapter; 33 | private OutlineActionMode actionMode; 34 | private ActionMode activeActionMode = null; 35 | 36 | private int lastPositionClicked = -1; 37 | 38 | public OutlineListView(Context context, AttributeSet atts) { 39 | super(context, atts); 40 | this.context = activity; 41 | setChoiceMode(ListView.CHOICE_MODE_SINGLE); 42 | setOnItemClickListener(outlineClickListener); 43 | setOnItemLongClickListener(outlineLongClickListener); 44 | this.actionMode = new OutlineActionMode(context); 45 | setAdapter(new OutlineAdapter(context)); 46 | } 47 | 48 | public void setAdapter(OutlineAdapter adapter) { 49 | this.adapter = adapter; 50 | super.setAdapter(adapter); 51 | } 52 | 53 | 54 | public void setActivity(Activity activity) { 55 | this.activity = activity; 56 | this.context = activity; 57 | } 58 | 59 | 60 | public void saveState(Bundle outState) { 61 | outState.putLongArray(OUTLINE_NODES, adapter.getNodeState()); 62 | outState.putIntegerArrayList(OUTLINE_LEVELS, adapter.getLevelState()); 63 | outState.putBooleanArray(OUTLINE_EXPANDED, adapter.getExpandedState()); 64 | outState.putInt(OUTLINE_CHECKED_POS, getCheckedItemPosition()); 65 | outState.putInt(OUTLINE_SCROLL_POS, getFirstVisiblePosition()); 66 | } 67 | 68 | public void loadState(Bundle savedInstanceState) { 69 | long[] state = savedInstanceState.getLongArray(OUTLINE_NODES); 70 | ArrayList levels = savedInstanceState.getIntegerArrayList(OUTLINE_LEVELS); 71 | boolean[] expanded = savedInstanceState.getBooleanArray(OUTLINE_EXPANDED); 72 | 73 | if(state != null) { 74 | try { 75 | this.adapter.setState(state, levels, expanded); 76 | } catch (IllegalStateException ex) { 77 | this.adapter.init(); 78 | } 79 | } 80 | 81 | int checkedPos= savedInstanceState.getInt(OUTLINE_CHECKED_POS, 0); 82 | setItemChecked(checkedPos, true); 83 | 84 | int scrollPos = savedInstanceState.getInt(OUTLINE_SCROLL_POS, 0); 85 | setSelection(scrollPos); 86 | } 87 | 88 | 89 | public void refresh() { 90 | int position = getFirstVisiblePosition(); 91 | this.adapter.refresh(); 92 | setSelection(position); 93 | } 94 | 95 | public long getCheckedNodeId() { 96 | if(getCheckedItemPosition() == ListView.INVALID_POSITION) 97 | return -1; 98 | else { 99 | int position = getCheckedItemPosition(); 100 | return adapter.getItemId(position); 101 | } 102 | } 103 | 104 | public void setData(List nodes) { 105 | adapter.clear(); 106 | if (nodes.size() > 0) 107 | adapter.insertAll(nodes, 0); 108 | } 109 | 110 | private OnItemClickListener outlineClickListener = new OnItemClickListener() { 111 | @Override 112 | public void onItemClick(AdapterView parent, View v, int position, 113 | long id) { 114 | if(activeActionMode != null) 115 | activeActionMode.finish(); 116 | 117 | OrgNode node = adapter.getItem(position); 118 | if(node.getDisplayChildren().size() > 0) { 119 | if (PreferenceUtils.outlineExpandAll()) { 120 | boolean doubleClicked = lastPositionClicked == position; 121 | boolean collapsed = adapter.collapseExpandExpandAll(position, doubleClicked); 122 | if (collapsed) { 123 | lastPositionClicked = -1; 124 | return; 125 | } 126 | } 127 | else 128 | adapter.collapseExpand(position); 129 | } 130 | else { 131 | boolean viewOnClick = PreferenceManager 132 | .getDefaultSharedPreferences(context).getBoolean( 133 | "viewOnClick", false); 134 | 135 | if (node.type == OrgNode.Type.Checkbox) { 136 | OrgNodeUtils.toggleCheckbox(node); 137 | adapter.notifyDataSetInvalidated(); 138 | return; 139 | } 140 | 141 | 142 | if (viewOnClick) 143 | OutlineActionMode.runViewNodeActivity(node.Id, context); 144 | else 145 | OutlineActionMode.runEditNodeActivity(node.Id, context); 146 | //setParentChecked(position); 147 | } 148 | 149 | lastPositionClicked = position; 150 | } 151 | }; 152 | 153 | private OnItemLongClickListener outlineLongClickListener = new OnItemLongClickListener() { 154 | @Override 155 | public boolean onItemLongClick(AdapterView parent, View v, int position, 156 | long id) { 157 | 158 | showEditDialog(position); 159 | // if(activity != null) { 160 | // actionMode.initActionMode(OutlineListView.this, position); 161 | // activeActionMode = activity.startActionMode(actionMode); 162 | // } 163 | return true; 164 | } 165 | }; 166 | 167 | private void showEditDialog(int position) { 168 | if (position < 0) 169 | return; 170 | 171 | OrgNode node = (OrgNode) getAdapter().getItem(position); 172 | if (node == null) 173 | return; 174 | 175 | BaseEditFragment fragment = BaseEditFragment.getEditFragment(node); 176 | fragment.show(activity); 177 | } 178 | 179 | @SuppressWarnings("unused") 180 | private void setParentChecked(int position) { 181 | int parentPos = adapter.findParent(position); 182 | if(parentPos >= 0) 183 | setItemChecked(parentPos, true); 184 | } 185 | 186 | public void collapseCurrent() { 187 | int position = getCheckedItemPosition(); 188 | 189 | if (position == ListView.INVALID_POSITION) 190 | return; 191 | 192 | if (adapter.getExpanded(position)) // Item is expanded, collapse it 193 | adapter.collapseExpand(position); 194 | else { 195 | if(adapter.getLevel(position) == 0) { // Top level, collapse all entries 196 | adapter.collapseAll(); 197 | setItemChecked(position, false); 198 | } else { // Collapse parent 199 | int parent = adapter.findParent(position); 200 | 201 | if (parent >= 0) { 202 | adapter.collapseExpand(parent); 203 | setItemChecked(parent, true); 204 | } 205 | } 206 | } 207 | 208 | ensureCheckedItemVisible(); 209 | } 210 | 211 | public void ensureCheckedItemVisible() { 212 | int position = getCheckedItemPosition(); 213 | if(position == ListView.INVALID_POSITION) 214 | return; 215 | 216 | if(!(getLastVisiblePosition() >= position && getFirstVisiblePosition() <= position)) 217 | setSelection(position - 2); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/gui/outline/OutlineAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.gui.outline; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ArrayAdapter; 7 | 8 | import com.hdweiss.morgand.R; 9 | import com.hdweiss.morgand.data.dao.OrgNode; 10 | import com.hdweiss.morgand.data.dao.OrgNodeRepository; 11 | import com.hdweiss.morgand.gui.theme.DefaultTheme; 12 | import com.hdweiss.morgand.utils.Utils; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | import java.util.Collections; 17 | 18 | public class OutlineAdapter extends ArrayAdapter { 19 | 20 | private ArrayList expanded = new ArrayList(); 21 | private ArrayList level = new ArrayList(); 22 | 23 | private DefaultTheme theme; 24 | 25 | private boolean agendaMode = false; 26 | 27 | public OutlineAdapter(Context context) { 28 | super(context, R.layout.outline_item); 29 | 30 | this.theme = DefaultTheme.getTheme(context); 31 | init(); 32 | } 33 | 34 | public void init() { 35 | clear(); 36 | 37 | for (OrgNode node : OrgNodeRepository.getRootNodes()) 38 | add(node); 39 | 40 | notifyDataSetInvalidated(); 41 | } 42 | 43 | 44 | public long[] getNodeState() { 45 | int count = getCount(); 46 | long[] state = new long[count]; 47 | 48 | for(int i = 0; i < count; i++) 49 | state[i] = getItem(i).Id; 50 | 51 | return state; 52 | } 53 | 54 | public ArrayList getLevelState() { 55 | return this.level; 56 | } 57 | 58 | public boolean[] getExpandedState() { 59 | return Utils.toPrimitiveArray(this.expanded); 60 | } 61 | 62 | public void setState(long[] state, ArrayList levels, boolean[] expanded) { 63 | clear(); 64 | 65 | for(int i = 0; i < state.length; i++) { 66 | try { 67 | OrgNode node = OrgNodeRepository.queryForId((int) state[i]); 68 | if (node == null) 69 | throw new IllegalStateException("Give OutlineAdapter state is invalid, node with id " + state[i] + " not found"); 70 | 71 | add(node); 72 | } catch(Exception ex) {} 73 | } 74 | 75 | this.expanded.clear(); 76 | for(boolean expand: expanded) 77 | this.expanded.add(expand); 78 | this.level = levels; 79 | } 80 | 81 | public void refresh() { 82 | ArrayList expandedNodeIds = new ArrayList(); 83 | int size = this.expanded.size(); 84 | for(int i = 0; i < size; i++) { 85 | if(this.expanded.get(i)) 86 | expandedNodeIds.add(getItemId(i)); 87 | } 88 | 89 | init(); 90 | 91 | expandNodes(expandedNodeIds); 92 | } 93 | 94 | private void expandNodes(ArrayList nodeIds) { 95 | while (nodeIds.size() != 0) { 96 | Long nodeId = nodeIds.get(0); 97 | for (int nodesPosition = 0; nodesPosition < getCount(); nodesPosition++) { 98 | if (getItemId(nodesPosition) == nodeId) { 99 | expand(nodesPosition); 100 | break; 101 | } 102 | } 103 | nodeIds.remove(0); 104 | } 105 | } 106 | 107 | @Override 108 | public View getView(int position, View convertView, ViewGroup parent) { 109 | OutlineItemView outlineItemView = (OutlineItemView) convertView; 110 | if (convertView == null) 111 | outlineItemView = new OutlineItemView(getContext()); 112 | 113 | outlineItemView.setAgendaMode(agendaMode); 114 | 115 | OrgNode node = getItem(position); 116 | 117 | if (node != null) 118 | outlineItemView.setup(node, getExpanded(position), getLevel(position), theme); 119 | 120 | return outlineItemView; 121 | } 122 | 123 | public void setAgendaMode(boolean agendaMode) { 124 | this.agendaMode = agendaMode; 125 | } 126 | 127 | @Override 128 | public void clear() { 129 | super.clear(); 130 | this.expanded.clear(); 131 | this.level.clear(); 132 | } 133 | 134 | @Override 135 | public void add(OrgNode node) { 136 | super.add(node); 137 | this.expanded.add(false); 138 | this.level.add(0); 139 | } 140 | 141 | @Override 142 | public void insert(OrgNode node, int index) { 143 | super.insert(node, index); 144 | this.expanded.add(index, false); 145 | 146 | int level = index > 0 ? this.level.get(index - 1) + 1 : 0; 147 | this.level.add(index, level); 148 | } 149 | 150 | public void insertAll(Collection nodes, int position) { 151 | ArrayList orgNodes = new ArrayList(nodes); 152 | Collections.reverse(orgNodes); 153 | for(OrgNode node: orgNodes) 154 | insert(node, position); 155 | notifyDataSetInvalidated(); 156 | } 157 | 158 | @Override 159 | public void remove(OrgNode node) { 160 | int position = getPosition(node); 161 | this.expanded.remove(position); 162 | this.level.remove(position); 163 | super.remove(node); 164 | } 165 | 166 | public boolean getExpanded(int position) { 167 | if(position < 0 || position > this.expanded.size()) 168 | return false; 169 | 170 | return this.expanded.get(position); 171 | } 172 | 173 | public int getLevel(int position) { 174 | if (position < 0 || position > this.level.size()) 175 | return 0; 176 | 177 | return this.level.get(position); 178 | } 179 | 180 | public void collapseExpand(int position) { 181 | if(position >= getCount() || position >= this.expanded.size() || position < 0) 182 | return; 183 | 184 | if(this.expanded.get(position)) 185 | collapse(position); 186 | else 187 | expand(position); 188 | } 189 | 190 | /** 191 | * @param doubleClicked Whether user clicked node the second time 192 | * @return true expandAll was called 193 | */ 194 | public boolean collapseExpandExpandAll(int position, boolean doubleClicked) { 195 | if(position >= getCount() || position >= this.expanded.size() || position < 0) 196 | return false; 197 | 198 | if(this.expanded.get(position)) { 199 | if (doubleClicked) { 200 | expandAll(position); 201 | return true; 202 | } else { 203 | collapse(position); 204 | return false; 205 | } 206 | } 207 | else { 208 | expand(position); 209 | return false; 210 | } 211 | } 212 | 213 | public void collapse(int position) { 214 | int activePos = position + 1; 215 | while(activePos < this.expanded.size()) { 216 | if(getLevel(activePos) <= getLevel(position)) 217 | break; 218 | collapse(activePos); 219 | remove(getItem(activePos)); 220 | } 221 | this.expanded.set(position, false); 222 | } 223 | 224 | public void collapseAll() { 225 | for(int activePos = 0; activePos < expanded.size(); activePos++) { 226 | if (expanded.get(activePos)) 227 | collapse(activePos); 228 | } 229 | } 230 | 231 | 232 | public ArrayList expand(int position) { 233 | OrgNode node = getItem(position); 234 | ArrayList children = node.getDisplayChildren(); 235 | if (node.type == OrgNode.Type.Directory) 236 | Collections.sort(children, new OrgNode.OrgNodeCompare()); 237 | 238 | insertAll(children, position + 1); 239 | this.expanded.set(position, true); 240 | return children; 241 | } 242 | 243 | public void expandAll(int position) { 244 | collapse(position); // TODO Hack 245 | 246 | ArrayList expandedChildren = expand(position); 247 | 248 | for(OrgNode node: expandedChildren) { 249 | int nodePosition = getPosition(node); 250 | expandAll(nodePosition); 251 | } 252 | } 253 | 254 | @Override 255 | public long getItemId(int position) { 256 | return getItem(position).Id; 257 | } 258 | 259 | public int findParent(int position) { 260 | if(position >= getCount() || position < 0) 261 | return -1; 262 | 263 | for(int activePos = position - 1; activePos >= 0; activePos--) { 264 | if(getLevel(activePos) < getLevel(position)) 265 | return activePos; 266 | } 267 | 268 | return -1; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /mOrgAnd/src/main/java/com/hdweiss/morgand/settings/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.hdweiss.morgand.settings; 2 | 3 | import android.content.SharedPreferences; 4 | import android.content.pm.PackageManager; 5 | import android.os.Bundle; 6 | import android.preference.ListPreference; 7 | import android.preference.Preference; 8 | import android.preference.PreferenceActivity; 9 | import android.preference.PreferenceFragment; 10 | import android.preference.PreferenceManager; 11 | 12 | import com.hdweiss.morgand.R; 13 | import com.hdweiss.morgand.synchronizer.calendar.CalendarWrapper; 14 | 15 | import java.util.List; 16 | 17 | public class SettingsActivity extends PreferenceActivity { 18 | 19 | @Override 20 | public void onBuildHeaders(List

target) { 21 | loadHeadersFromResource(R.xml.pref_headers, target); 22 | } 23 | 24 | 25 | public static class GeneralPreferenceFragment extends PreferenceFragment { 26 | @Override 27 | public void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | addPreferencesFromResource(R.xml.pref_general); 30 | } 31 | 32 | @Override 33 | public void onResume() { 34 | super.onResume(); 35 | try { 36 | String versionName = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName; 37 | findPreference("version").setSummary(versionName); 38 | } catch (PackageManager.NameNotFoundException ex) { 39 | } 40 | } 41 | } 42 | 43 | public static class InterfacePreferenceFragment extends PreferenceFragment { 44 | @Override 45 | public void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | addPreferencesFromResource(R.xml.pref_interface); 48 | } 49 | } 50 | 51 | public static class DataSyncPreferenceFragment extends PreferenceFragment { 52 | @Override 53 | public void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | addPreferencesFromResource(R.xml.pref_data_sync); 56 | 57 | bindPreferenceSummaryToValue(findPreference("sync_mode")); 58 | bindPreferenceSummaryToValue(findPreference("sync_frequency")); 59 | bindPreferenceSummaryToValue(findPreference("todo_active")); 60 | bindPreferenceSummaryToValue(findPreference("todo_inactive")); 61 | bindPreferenceSummaryToValue(findPreference("priorities")); 62 | } 63 | } 64 | 65 | 66 | public static class GitPreferenceFragment extends PreferenceFragment { 67 | @Override 68 | public void onCreate(Bundle savedInstanceState) { 69 | super.onCreate(savedInstanceState); 70 | addPreferencesFromResource(R.xml.pref_git); 71 | 72 | bindPreferenceSummaryToValue(findPreference("git_commit_author")); 73 | bindPreferenceSummaryToValue(findPreference("git_commit_email")); 74 | bindPreferenceSummaryToValue(findPreference("git_key_info")); 75 | bindPreferenceSummaryToValue(findPreference("git_username")); 76 | bindPreferenceSummaryToValue(findPreference("git_local_path")); 77 | bindPreferenceSummaryToValue(findPreference("git_merge_strategy")); 78 | bindPreferenceSummaryToValue(findPreference("git_branch")); 79 | 80 | bindPreferenceSummaryToValue(findPreference("git_url")); 81 | findPreference("git_url").setOnPreferenceChangeListener(sResetGitLocalPathListener); 82 | } 83 | 84 | @Override 85 | public void onResume() { 86 | super.onResume(); 87 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); 88 | 89 | // Update setting keys that are modified by starting another activity 90 | findPreference("git_local_path").setSummary(preferences.getString("git_local_path", "")); 91 | findPreference("git_key_info").setSummary(preferences.getString("git_key_info", "")); 92 | } 93 | } 94 | 95 | 96 | public static class CalendarPreferenceFragment extends PreferenceFragment { 97 | @Override 98 | public void onCreate(Bundle savedInstanceState) { 99 | super.onCreate(savedInstanceState); 100 | addPreferencesFromResource(R.xml.pref_calendar); 101 | 102 | populateCalendarNames(); 103 | 104 | bindPreferenceSummaryToValue(findPreference("calendar_name")); 105 | bindPreferenceSummaryToValue(findPreference("calendar_reminder")); 106 | bindPreferenceSummaryToValue(findPreference("calendar_reminder_interval")); 107 | } 108 | 109 | private void populateCalendarNames() { 110 | try { 111 | ListPreference calendarName = (ListPreference) findPreference("calendar_name"); 112 | 113 | CharSequence[] calendars = CalendarWrapper 114 | .getCalendars(getActivity()); 115 | 116 | calendarName.setEntries(calendars); 117 | calendarName.setEntryValues(calendars); 118 | } catch (Exception e) { 119 | e.printStackTrace(); 120 | // Don't crash because of anything in calendar 121 | } 122 | } 123 | } 124 | 125 | private static Preference.OnPreferenceChangeListener sResetGitLocalPathListener = new Preference.OnPreferenceChangeListener() { 126 | @Override 127 | public boolean onPreferenceChange(Preference preference, Object value) { 128 | preference.setSummary(value.toString()); 129 | 130 | PreferenceUtils.set("git_local_path", ""); 131 | preference.getPreferenceManager().findPreference("git_local_path").setSummary(""); 132 | 133 | return true; 134 | } 135 | }; 136 | 137 | /** 138 | * A preference value change listener that updates the preference's summary to reflect its new 139 | * value. 140 | */ 141 | private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { 142 | @Override 143 | public boolean onPreferenceChange(Preference preference, Object value) { 144 | String stringValue = value.toString(); 145 | 146 | if (preference instanceof ListPreference) { 147 | // For list preferences, look up the correct display value in 148 | // the preference's 'entries' list. 149 | ListPreference listPreference = (ListPreference) preference; 150 | int index = listPreference.findIndexOfValue(stringValue); 151 | 152 | // Set the summary to reflect the new value. 153 | preference.setSummary( 154 | index >= 0 155 | ? listPreference.getEntries()[index] 156 | : null 157 | ); 158 | } else { 159 | // For all other preferences, set the summary to the value's 160 | // simple string representation. 161 | preference.setSummary(stringValue); 162 | } 163 | return true; 164 | } 165 | }; 166 | 167 | /** 168 | * Binds a preference's summary to its value. More specifically, when the preference's value is 169 | * changed, its summary (line of text below the preference title) is updated to reflect the 170 | * value. The summary is also immediately updated upon calling this method. The exact display 171 | * format is dependent on the type of preference. 172 | * 173 | * @see #sBindPreferenceSummaryToValueListener 174 | */ 175 | private static void bindPreferenceSummaryToValue(Preference preference) { 176 | if (preference == null) 177 | return; 178 | 179 | // Set the listener to watch for value changes. 180 | preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); 181 | 182 | // Trigger the listener immediately with the preference's 183 | // current value. 184 | 185 | if (preference.getKey().equals("calendar_reminder")) { 186 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, 187 | PreferenceManager 188 | .getDefaultSharedPreferences(preference.getContext()) 189 | .getBoolean(preference.getKey(), false) 190 | ); 191 | } else { 192 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, 193 | PreferenceManager 194 | .getDefaultSharedPreferences(preference.getContext()) 195 | .getString(preference.getKey(), "") 196 | ); 197 | } 198 | } 199 | } 200 | --------------------------------------------------------------------------------