diff options
175 files changed, 3656 insertions, 1273 deletions
@@ -51,12 +51,11 @@ If the workspace directory name contains a space and leads to errors in the -dex 3. copy `./main/templates/local.properties` to `./main/` 4. copy `./main/templates/local.properties` to `./tests/` 5. edit `local.properties` (see comments in the file) -6. copy `./main/templates/mapsapikey.xml` to `./main/res/values/` -7. edit `./main/res/values/mapsapikey.xml` and insert your Maps API key (see comments in the file) -8. copy `./main/templates/ocde_okapi.xml` to `./main/res/values/` -9. request your personal ConsumerKey and -Secret at [opencaching.de OKAPI signup](http://www.opencaching.de/okapi/signup.html) -10. edit `./main/res/values/ocde_okapi.xml` and insert your keys there. -11. Repeat steps 8-10 with `ocpl_okapi.xml` and [opencaching.pl OKAPI signup](http://www.opencaching.pl/okapi/signup.html) +6. copy `./main/templates/keys.xml` to `./main/res/values/` +7. edit `./main/res/values/keys.xml` and insert several keys (see comments in the file) +7.a) Google Maps API key +7.b) request your personal consumer key and secret at [opencaching.de OKAPI signup](http://www.opencaching.de/okapi/signup.html) +7.c) request your personal consumer key and secret at [opencaching.pl OKAPI signup](http://www.opencaching.pl/okapi/signup.html) ### Building with Ant ### diff --git a/cgeo-calendar/.settings/edu.umd.cs.findbugs.core.prefs b/cgeo-calendar/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 0000000..a16868e --- /dev/null +++ b/cgeo-calendar/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,133 @@ +#FindBugs User Preferences +#Sun Dec 08 19:08:28 CET 2013 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|true +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=3 +effort=max +excludefilter0=../main/project/findbugs/exclusions.xml|true +filter_settings=Low|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|20 +filter_settings_neg=NOISE,I18N| +run_at_full_build=true diff --git a/cgeo-calendar/project.properties b/cgeo-calendar/project.properties index 54e1b7d..705eae9 100644 --- a/cgeo-calendar/project.properties +++ b/cgeo-calendar/project.properties @@ -11,4 +11,4 @@ proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=Google Inc.:Google APIs:4 +target=Google Inc.:Google APIs:8 diff --git a/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java b/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java index 6c7624d..ff36aa4 100644 --- a/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java +++ b/cgeo-calendar/src/cgeo/calendar/CalendarEntry.java @@ -35,7 +35,7 @@ class CalendarEntry { final String startTime = getParameter(ICalendar.PARAM_START_TIME_MINUTES); if (startTime.length() > 0) { try { - this.startTimeMinutes = Integer.valueOf(startTime); + this.startTimeMinutes = Integer.parseInt(startTime); } catch (NumberFormatException e) { Log.e("CalendarEntry creation", e); } diff --git a/main/.classpath b/main/.classpath index d7bb252..1b83665 100644 --- a/main/.classpath +++ b/main/.classpath @@ -20,5 +20,6 @@ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/> <classpathentry kind="lib" path="compile-libs/org.eclipse.jdt.annotation_1.1.0.v20130513-1648.jar"/> + <classpathentry kind="lib" path="compile-libs/findbugs-jsr305.jar"/> <classpathentry kind="output" path="bin/classes"/> </classpath> diff --git a/main/.factorypath b/main/.factorypath index 35973be..f18f942 100644 --- a/main/.factorypath +++ b/main/.factorypath @@ -1,4 +1,4 @@ <factorypath> - <factorypathentry kind="WKSPJAR" id="/cgeo/libs/butterknife-4.0.0.jar" enabled="true" runInBatchMode="false"/> + <factorypathentry kind="WKSPJAR" id="/cgeo/libs/butterknife-4.0.1.jar" enabled="true" runInBatchMode="false"/> <factorypathentry kind="WKSPJAR" id="/cgeo/compile-libs/androidannotations-2.7.1.jar" enabled="true" runInBatchMode="false"/> </factorypath> diff --git a/main/.settings/edu.umd.cs.findbugs.core.prefs b/main/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 0000000..36ab342 --- /dev/null +++ b/main/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,133 @@ +#FindBugs User Preferences +#Sun Dec 08 19:08:28 CET 2013 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|true +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=3 +effort=max +excludefilter0=project/findbugs/exclusions.xml|true +filter_settings=Low|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|20 +filter_settings_neg=NOISE,I18N| +run_at_full_build=true diff --git a/main/build.xml b/main/build.xml index 40be059..257aaf3 100644 --- a/main/build.xml +++ b/main/build.xml @@ -95,32 +95,26 @@ <if condition="${build.mode.release}"> <then> - <filterset id="build-tokens"> + <filterset id="maps-key"> <filter token="maps.api.key" value="${maps.api.key.market}"/> </filterset> </then> <else> - <filterset id="build-tokens"> + <filterset id="maps-key"> <filter token="maps.api.key" value="${maps.api.key}"/> </filterset> </else> </if> - <copy file="./templates/mapsapikey.xml" todir="./res/values/" overwrite="true"> - <filterset refid="build-tokens" /> - </copy> - <copy file="./templates/ocde_okapi.xml" todir="./res/values/" overwrite="true"> + <copy file="./templates/keys.xml" todir="./res/values/" overwrite="true"> + <filterset refid="maps-key" /> <filterset> <filter token="ocde.okapi.consumer.key" value="${ocde.okapi.consumer.key}"/> <filter token="ocde.okapi.consumer.secret" value="${ocde.okapi.consumer.secret}"/> - </filterset> - </copy> - <copy file="./templates/ocpl_okapi.xml" todir="./res/values/" overwrite="true"> - <filterset> <filter token="ocpl.okapi.consumer.key" value="${ocpl.okapi.consumer.key}"/> <filter token="ocpl.okapi.consumer.secret" value="${ocpl.okapi.consumer.secret}"/> </filterset> </copy> - </target> + </target> <!-- start of modifications for android annotations, see https://github.com/excilys/androidannotations/wiki/Building-Project-Ant --> <property name="generated.dir" value="annotation_gen" /> @@ -174,6 +168,7 @@ outdir="${out.absolute.dir}/classes" metadatafile="${emma.coverage.absolute.file}"> <filter excludes="${emma.default.filter}" /> + <filter excludes="android.*, com.*, org.*" /> <filter value="${emma.filter}" /> </instr> </emma> diff --git a/main/compile-libs/findbugs-jsr305.jar b/main/compile-libs/findbugs-jsr305.jar Binary files differnew file mode 100644 index 0000000..cc39b73 --- /dev/null +++ b/main/compile-libs/findbugs-jsr305.jar diff --git a/main/libs/android-support-v4.jar b/main/libs/android-support-v4.jar Binary files differindex 1fbeba0..9056828 100644 --- a/main/libs/android-support-v4.jar +++ b/main/libs/android-support-v4.jar diff --git a/main/libs/butterknife-4.0.0.jar b/main/libs/butterknife-4.0.0.jar Binary files differdeleted file mode 100644 index c3fc259..0000000 --- a/main/libs/butterknife-4.0.0.jar +++ /dev/null diff --git a/main/libs/butterknife-4.0.1.jar b/main/libs/butterknife-4.0.1.jar Binary files differnew file mode 100644 index 0000000..e048516 --- /dev/null +++ b/main/libs/butterknife-4.0.1.jar diff --git a/main/libs/findbugs-annotations.jar b/main/libs/findbugs-annotations.jar Binary files differnew file mode 100644 index 0000000..3641ad6 --- /dev/null +++ b/main/libs/findbugs-annotations.jar diff --git a/main/project/attributes/iconlist.txt b/main/project/attributes/iconlist.txt index 37ddf1b..382c3e8 100644 --- a/main/project/attributes/iconlist.txt +++ b/main/project/attributes/iconlist.txt @@ -80,7 +80,7 @@ camping | 31 | | | | PD | USA National Park Serv stroller | 41 | | | | PD | USA National Park Service | http://thenounproject.com/noun/stroller/#icon-No161 fuel | 58 | | | | PD | | http://thenounproject.com/noun/gas/#icon-No155 food | 59 | | | | PD | Roger Cook, Don Shanosky | http://thenounproject.com/noun/restaurant/#icon-No33 -oc_only | | 6 | | x | PD | koem | selfmade +oc_only | | 6 | 1 | x | PD | koem | selfmade link_only | | 7 | | x | PD | koem | selfmade letterbox | | 8 | 4 | | CC0 | | http://thenounproject.com/noun/post-office/#icon-No2404 railway | | 10 | 60 | | CC0 | | http://thenounproject.com/noun/train/#icon-No4420 @@ -112,20 +112,20 @@ ask_owner | | 58 | 17 | x | PD | koem # unknown | -1 | -1 | -1 | x | PD | koem | selfmade geotour | 67 | | | | CC0 | James Keuning | http://thenounproject.com/noun/suitcase/#icon-No9097 +kids_2 | | | 70 | x | PD | | http://thenounproject.com/noun/teddy-bear/#icon-No6843 +historic_site | | | 29 | | PD | Reuben | http://thenounproject.com/term/castle/12390/ +magnetic | | | 6 | | PD | | http://thenounproject.com/term/magnet/698/ +usb_cache | | | 10 | | CCBY | Kenneth van Alt | http://thenounproject.com/term/usb/1824/ ### In need of icons survey_marker | | | 2 | wherigo | | | 3 | geohotel | | | 5 | -magnetic | | | 6 | audio_cache | | | 7 | offset_cache | | | 8 | -usb_cache | | | 10 | pedestrian_only | | | 20 | nature_cache | | | 28 | -historic_site | | | 29 | byop | | | 50 | shovel | | | 51 | quick_cache | | | 68 | -kids | | | 70 | safari_cache | | | 72 | specific_access | | | 73 | diff --git a/main/project/attributes/svgs/historic_site.svg b/main/project/attributes/svgs/historic_site.svg new file mode 100644 index 0000000..fef0ab3 --- /dev/null +++ b/main/project/attributes/svgs/historic_site.svg @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + width="100px" + height="100px" + viewBox="0 0 100 100" + enable-background="new 0 0 100 100" + xml:space="preserve" + id="svg2" + inkscape:version="0.48.4 r9939" + sodipodi:docname="historic_site.svg"><metadata + id="metadata12"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs10" /><sodipodi:namedview + pagecolor="#009674" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + inkscape:window-width="1878" + inkscape:window-height="1060" + id="namedview8" + showgrid="false" + inkscape:zoom="6.675088" + inkscape:cx="35.113858" + inkscape:cy="49.767912" + inkscape:window-x="-2" + inkscape:window-y="-3" + inkscape:window-maximized="1" + inkscape:current-layer="Your_Icon" /><g + id="Captions" /><g + id="Your_Icon"><path + clip-rule="evenodd" + d="M65.873,61.23h8.231v-5.849l-0.355-1.43l-0.596-1.314l-0.956-1.192l-0.955-0.956 l-1.192-0.835h-0.122l-1.07,0.835l-0.959,0.956l-0.952,1.192l-0.596,1.314l-0.359,1.43l-0.118,1.433V61.23 M100,96.308H0v-5.01 l8.351-6.681V26.149h8.235V3H28.28v13.244h10.027V3h11.691v34.96h8.354V22.926h11.575V37.96h10.027V22.926h11.694v61.691 L100,91.298V96.308z M16.467,47.149l-0.359,0.357l-0.475,0.239l-0.596,0.241l-0.6-0.241l-0.478-0.239l-0.355-0.357l-0.241-0.596 v-0.836l0.241-0.597l0.478-0.239l0.596-0.237h0.596l0.478,0.237l0.478,0.361l0.237,0.235h5.969v-6.084l-0.359-0.357L21.84,38.44 l-0.122-0.479l0.122-0.596l0.237-0.476l0.359-0.357l0.596-0.361h0.24l0.711,0.239l0.481,0.24l0.355,0.357l0.119,0.718v0.474 l-0.119,0.479l-0.478,0.474l-0.241,0.241v6.084h6.088l0.359-0.476l0.596-0.357h1.074l0.478,0.357l0.718,0.955v0.356l-0.122,0.598 l-0.359,0.478l-0.478,0.357l-0.596,0.241h-0.474l-0.596-0.241l-0.359-0.478l-0.24-0.119h-6.088v5.967l0.481,0.475l0.356,0.361 v1.311l-0.356,0.357l-0.481,0.357l-0.474,0.241h-0.956l-0.359-0.359l-0.474-0.355l-0.122-0.599V54.31l0.122-0.479l0.474-0.475 l0.122-0.239v-5.967H16.467z M16.586,82.943h13.368v-8.469l-0.119-1.791l-0.478-1.907l-0.596-1.79l-0.837-1.672l-1.196-1.55 l-1.311-1.311l-1.433-1.194l-0.711-0.361h0.115l-1.548,1.076l-1.433,1.311l-1.314,1.55l-0.955,1.555l-0.715,1.669l-0.478,1.912 l-0.24,1.907l-0.119,0.597V82.943z" + id="path6" + fill-rule="evenodd" + style="fill-rule:evenodd;fill:#ffffff;fill-opacity:1" /></g></svg>
\ No newline at end of file diff --git a/main/project/attributes/svgs/kids_2.svg b/main/project/attributes/svgs/kids_2.svg new file mode 100644 index 0000000..6f9849d --- /dev/null +++ b/main/project/attributes/svgs/kids_2.svg @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Bear" + x="0px" + y="0px" + width="63.230682" + height="68.21875" + viewBox="0 0 266.51217 287.87016" + enable-background="new 0 0 309.361 421.981" + xml:space="preserve" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="kids.svg"><metadata + id="metadata20"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs18" /><sodipodi:namedview + pagecolor="#009674" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + inkscape:window-width="640" + inkscape:window-height="480" + id="namedview16" + showgrid="false" + inkscape:zoom="2.36" + inkscape:cx="31.638548" + inkscape:cy="18.21875" + inkscape:window-x="49" + inkscape:window-y="24" + inkscape:window-maximized="0" + inkscape:current-layer="Bear" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" /> + + +<path + id="Arm" + d="m 231.35965,276.886 c -18.414,0 -35.671,-16.001 -41.033,-38.047 -2.884,-11.853 -2.05,-23.97 2.35,-34.12 4.744,-10.944 13.396,-18.665 23.736,-21.18 2.461,-0.599 4.998,-0.903 7.539,-0.903 18.413,0 35.669,16.001 41.031,38.047 6.197,25.486 -5.505,50.294 -26.085,55.301 -2.463,0.598 -4.999,0.902 -7.538,0.902 z m -7.408,-74.25 c -0.949,0 -1.895,0.113 -2.811,0.337 -5.748,1.397 -8.804,6.678 -10.114,9.7 -2.64,6.088 -3.101,13.902 -1.267,21.438 3.158,12.984 12.443,22.774 21.6,22.774 0.948,0 1.895,-0.113 2.811,-0.336 9.698,-2.359 14.909,-16.62 11.379,-31.142 -3.159,-12.98 -12.443,-22.771 -21.598,-22.771 z" + style="fill:#ffffff" + inkscape:connector-curvature="0" /> +<path + id="Arm_1_" + d="m 35.015646,276.842 c -2.666,0 -5.323,-0.334 -7.898,-0.992 -10.31,-2.633 -18.8719996,-10.451 -23.4899996,-21.451 -4.28200004,-10.199 -4.977,-22.326 -1.958,-34.146 5.649,-22.121 22.5479996,-37.572 41.0949996,-37.572 2.666,0 5.324,0.334 7.899,0.992 10.309,2.632 18.871,10.45 23.489,21.451 4.282,10.199 4.978,22.325 1.958,34.146 -5.65,22.121 -22.55,37.572 -41.095,37.572 z m 7.749,-74.162 c -9.102,0 -18.438,9.683 -21.717,22.522 -1.92,7.517 -1.548,15.336 1.02,21.454 1.275,3.038 4.271,8.353 9.999,9.815 0.959,0.245 1.951,0.37 2.949,0.37 9.101,0 18.438,-9.683 21.718,-22.523 1.919,-7.516 1.547,-15.335 -1.021,-21.454 -1.275,-3.038 -4.27,-8.353 -9.998,-9.814 -0.961,-0.245 -1.952,-0.37 -2.95,-0.37 z" + style="fill:#ffffff" + inkscape:connector-curvature="0" /> +<path + id="Ear_1_" + d="m 230.97965,7.918 c -7.822,-5.02 -16.834,-7.785 -25.375,-7.785 -11.742,0 -21.72,5.052 -27.374,13.862 -10.175,15.857 -2.95,38.032 16.448,50.484 7.821,5.021 16.833,7.785 25.374,7.785 11.742,0 21.72,-5.052 27.375,-13.862 10.177,-15.857 2.952,-38.031 -16.448,-50.484 z m 2.245,44.652 c -5.7,8.881 -20.346,9.645 -32.714,1.707 -12.368,-7.939 -17.774,-21.57 -12.076,-30.451 5.698,-8.878 20.345,-9.643 32.713,-1.705 12.368,7.939 17.775,21.571 12.077,30.449 z" + style="fill:#ffffff" + inkscape:connector-curvature="0" /> +<path + id="Ear" + d="M 88.581646,13.566 C 82.915646,4.945 73.043646,0 61.496646,0 c -8.678,0 -17.813,2.863 -25.723,8.06 -8.455,5.555 -14.928,13.342 -18.226,21.927 -3.883,10.106 -3.036,20.579 2.322,28.732 5.667,8.623 15.54,13.567 27.087,13.567 8.677,0 17.812,-2.862 25.722,-8.059 8.456,-5.556 14.929,-13.343 18.227,-21.928 3.881,-10.105 3.034,-20.577 -2.324,-28.733 z m -21.866,40.543 c -12.282,8.07 -26.934,7.464 -32.729,-1.354 -5.794,-8.816 -0.534,-22.508 11.748,-30.578 12.283,-8.07 26.935,-7.465 32.729,1.352 5.794,8.819 0.536,22.51 -11.748,30.58 z" + style="fill:#ffffff" + inkscape:connector-curvature="0" /> + +<path + style="fill:#ffffff" + d="m 133.01968,14.373728 c -57.138004,0 -103.385349,38.978583 -103.385349,87.033582 0,31.446 19.729899,58.95528 49.450898,74.24228 -8.687998,5.788 -16.568691,12.98492 -23.472693,21.23092 10.250999,7.369 15.028263,23.44933 10.813263,39.95633 -5.135998,20.104 -21.586467,33.15993 -36.791468,29.27493 -0.914998,-0.23302 -1.906249,-0.44822 -2.769251,-0.79122 -0.587,5.17301 -0.791214,10.46929 -0.791214,15.82429 0,2.27882 0.02321,4.47753 0.131869,6.72532 l 34.285956,0 c 6.904677,-39.25292 37.018119,-68.83565 73.055459,-68.83565 36.03719,0 66.02009,29.58347 72.92359,68.83565 l 33.36288,0 c 0.10706,-2.23116 0.26373,-4.46354 0.26373,-6.72532 0,-5.303 -0.34809,-10.56842 -0.92308,-15.69242 -0.853,0.32501 -1.73638,0.57222 -2.63738,0.79122 -15.248,3.709 -31.62373,-9.63941 -36.52773,-29.80241 -3.945,-16.214 0.77639,-31.84598 10.68139,-39.29698 -6.95699,-8.358 -14.95443,-15.64366 -23.73643,-21.49466 29.723,-15.287 49.58277,-42.79528 49.58277,-74.24228 0,-48.054999 -46.37921,-87.033582 -103.51721,-87.033582 z M 98.60185,93.758903 c 5.257,0 9.49457,4.237572 9.49457,9.494577 0,5.257 -4.23757,9.62644 -9.49457,9.62644 -5.256001,0 -9.626441,-4.36944 -9.626441,-9.62644 0,-5.257005 4.37044,-9.494577 9.626441,-9.494577 z m 69.495,0 c 5.256,0 9.62644,4.237572 9.62644,9.494577 0,5.257 -4.37044,9.62644 -9.62644,9.62644 -5.257,0 -9.49458,-4.36944 -9.49458,-9.62644 0,-5.257005 4.23758,-9.494577 9.49458,-9.494577 z m -34.81344,15.033077 c 23.334,0 42.1981,15.24522 42.1981,34.02221 0,18.777 -18.8641,34.02222 -42.1981,34.02222 -23.334,0 -42.198096,-15.24522 -42.198096,-34.02222 0,-18.77699 18.864096,-34.02221 42.198096,-34.02221 z" + id="Top" + inkscape:connector-curvature="0" /> +<ellipse + id="Nose" + cx="155.388" + cy="131.813" + rx="18" + ry="11.75" + style="fill:#ffffff" + sodipodi:cx="155.388" + sodipodi:cy="131.813" + sodipodi:rx="18" + sodipodi:ry="11.75" + transform="translate(-21.351354,0)" /> +<path + d="m 135.36965,143.063 c -0.002,4.049 0.334,7.935 0.334,12" + id="path12" + style="fill:#ffffff" + inkscape:connector-curvature="0" /> + +<path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:4.9000001;stroke-miterlimit:4;stroke-dasharray:none" + id="path2988" + sodipodi:cx="30.084745" + sodipodi:cy="67.58316" + sodipodi:rx="14.40678" + sodipodi:ry="13.347458" + d="m 44.491526,67.58316 a 14.40678,13.347458 0 1 1 -0.01517,-0.612409 L 30.084745,67.58316 z" + transform="matrix(4.4545058,0,0,4.2198099,-0.25809025,-3.5761101)" + sodipodi:start="0" + sodipodi:end="6.2372871" /></svg>
\ No newline at end of file diff --git a/main/project/attributes/svgs/magnetic.svg b/main/project/attributes/svgs/magnetic.svg new file mode 100644 index 0000000..b6be067 --- /dev/null +++ b/main/project/attributes/svgs/magnetic.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + id="Layer_1" + x="0px" + y="0px" + width="69.938px" + height="100px" + viewBox="-0.116 0 69.938 100" + enable-background="new -0.116 0 69.938 100" + xml:space="preserve" + inkscape:version="0.48.4 r9939" + sodipodi:docname="icon_698.svg"><metadata + id="metadata11"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs9" /><sodipodi:namedview + pagecolor="#009674" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + inkscape:window-width="1109" + inkscape:window-height="1053" + id="namedview7" + showgrid="false" + inkscape:zoom="2.36" + inkscape:cx="-48.929303" + inkscape:cy="50" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="Layer_1" /><polygon + id="rect3074_1_" + points="5.242,85.721 24.511,85.721 25.837,100 6.568,100 " + style="fill:#ffffff;fill-opacity:1" /><polygon + id="rect3074_2_" + points="63.139,100 43.87,100 45.196,85.721 64.464,85.721 " + style="fill:#ffffff;fill-opacity:1" /><path + d="M34.916,0.001c-0.021,0-0.042,0-0.062,0s-0.04,0-0.062,0C19.221-0.138-2.174,8.982,0.043,29.5l5.098,54.671l19.167-0.041 l-5.304-53.406c-0.344-9.466,6.577-12.831,15.608-13.04c0.08,0.001,0.16,0.001,0.24,0c0.081,0.001,0.161,0.001,0.241,0 c9.031,0.209,15.952,3.574,15.608,13.04L45.399,84.13l19.167,0.041L69.664,29.5C71.881,8.982,50.486-0.138,34.916,0.001z" + id="path5" + style="fill:#ffffff;fill-opacity:1" /></svg>
\ No newline at end of file diff --git a/main/project/attributes/svgs/usb_cache.svg b/main/project/attributes/svgs/usb_cache.svg new file mode 100644 index 0000000..ef3cf03 --- /dev/null +++ b/main/project/attributes/svgs/usb_cache.svg @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Thumb_Drive" + x="0px" + y="0px" + width="65.8333333333px" + height="100px" + viewBox="0 0 79.205 120.489" + enable-background="new 0 0 79.205 120.489" + xml:space="preserve" + inkscape:version="0.48.4 r9939" + sodipodi:docname="icon_1824.svg"><metadata + id="metadata13"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs11" /><sodipodi:namedview + pagecolor="#009674" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + inkscape:window-width="1071" + inkscape:window-height="680" + id="namedview9" + showgrid="false" + inkscape:zoom="2.36" + inkscape:cx="32.916668" + inkscape:cy="50" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="Thumb_Drive" /><path + d="M39.584,28.458V7.556H28.587c-0.515,0-0.932,0.417-0.932,0.932v19.97H39.584z M31.854,17.571 c0-0.354,0.288-0.642,0.643-0.642h3.025c0.354,0,0.642,0.288,0.642,0.642v2.056c0,0.354-0.288,0.642-0.642,0.642h-3.025 c-0.354,0-0.643-0.287-0.643-0.642V17.571z" + id="path3" + style="fill:#ffffff;fill-opacity:1" /><path + d="M52.429,28.458V8.488c0-0.515-0.417-0.932-0.931-0.932H40.491v20.901H52.429z M43.921,17.571 c0-0.354,0.286-0.642,0.642-0.642h3.025c0.355,0,0.643,0.288,0.643,0.642v2.056c0,0.354-0.287,0.642-0.643,0.642h-3.025 c-0.355,0-0.642-0.287-0.642-0.642V17.571z" + id="path5" + style="fill:#ffffff;fill-opacity:1" /><path + d="M59.385,105.591c0,1.418-1.149,2.566-2.568,2.566h-33.55c-1.418,0-2.568-1.148-2.568-2.566V31.753 c0-1.418,1.15-2.568,2.568-2.568h33.55c1.419,0,2.568,1.15,2.568,2.568V105.591z M50.948,54.101h-5.305v5.305h1.709v5.286 c0,0.407-0.102,0.452-0.275,0.649l-6.003,5.732V49.679h2.138l-3.059-6.116l-3.058,6.116h2.137v28.188l-6.172-5.894 c-0.173-0.197-0.275-0.243-0.275-0.649v-5.449c1.052-0.377,1.805-1.381,1.805-2.562c0-1.506-1.221-2.727-2.726-2.727 c-1.506,0-2.727,1.221-2.727,2.727c0,1.182,0.754,2.186,1.805,2.562v6.195c0,0.323,0.125,0.503,0.272,0.645l8.018,7.697v4.25 c-2.102,0.428-3.684,2.285-3.684,4.514c0,2.543,2.062,4.605,4.605,4.605c2.544,0,4.606-2.062,4.606-4.605 c0-2.229-1.582-4.086-3.685-4.514V73.616l7.849-7.534c0.146-0.142,0.271-0.321,0.271-0.646v-6.031h1.754V54.101z" + id="path7" + style="fill:#ffffff;fill-opacity:1" /></svg>
\ No newline at end of file diff --git a/main/project/findbugs/exclusions.xml b/main/project/findbugs/exclusions.xml index c48983e..798ecb4 100644 --- a/main/project/findbugs/exclusions.xml +++ b/main/project/findbugs/exclusions.xml @@ -2,7 +2,10 @@ <FindBugsFilter> <!-- generated Android resources --> <Match> - <Class name="~.*\.R\$.*"/> + <Or> + <Class name="~.*\.R\$.*"/> + <Class name="~.*ViewHolder"/> + </Or> </Match> <!-- third party code --> @@ -22,6 +25,8 @@ <Bug pattern="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" /> </Or> </Match> + + <!-- Memory usage improvement by explicitly removing shared substrings in pattern matcher. --> <Match> <Bug pattern="DM_STRING_CTOR" /> <Or> @@ -29,9 +34,17 @@ <Class name="~.*TextUtils"></Class> </Or> </Match> + + <!-- File scanner needs to use hard coded names. --> <Match> <Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME"/> <Class name="~.*LocalStorage"/> </Match> - + + <!-- Tests using setUp() methods don't initialize their fields in the constructor. --> + <Match> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + <Class name="~.*Test"/> + </Match> + </FindBugsFilter>
\ No newline at end of file diff --git a/main/res/drawable-hdpi/ic_menu_account_list.png b/main/res/drawable-hdpi/ic_menu_account_list.png Binary files differnew file mode 100644 index 0000000..f858d2c --- /dev/null +++ b/main/res/drawable-hdpi/ic_menu_account_list.png diff --git a/main/res/drawable-ldpi/ic_menu_account_list.png b/main/res/drawable-ldpi/ic_menu_account_list.png Binary files differnew file mode 100644 index 0000000..04ededd --- /dev/null +++ b/main/res/drawable-ldpi/ic_menu_account_list.png diff --git a/main/res/drawable-mdpi/attribute_historic_site.png b/main/res/drawable-mdpi/attribute_historic_site.png Binary files differnew file mode 100644 index 0000000..9a9a7dd --- /dev/null +++ b/main/res/drawable-mdpi/attribute_historic_site.png diff --git a/main/res/drawable-mdpi/attribute_kids_2.png b/main/res/drawable-mdpi/attribute_kids_2.png Binary files differnew file mode 100644 index 0000000..be801af --- /dev/null +++ b/main/res/drawable-mdpi/attribute_kids_2.png diff --git a/main/res/drawable-mdpi/attribute_magnetic.png b/main/res/drawable-mdpi/attribute_magnetic.png Binary files differnew file mode 100644 index 0000000..a9f3c24 --- /dev/null +++ b/main/res/drawable-mdpi/attribute_magnetic.png diff --git a/main/res/drawable-mdpi/attribute_usb_cache.png b/main/res/drawable-mdpi/attribute_usb_cache.png Binary files differnew file mode 100644 index 0000000..e7a7d5f --- /dev/null +++ b/main/res/drawable-mdpi/attribute_usb_cache.png diff --git a/main/res/drawable-mdpi/ic_menu_account_list.png b/main/res/drawable-mdpi/ic_menu_account_list.png Binary files differnew file mode 100644 index 0000000..f0945b2 --- /dev/null +++ b/main/res/drawable-mdpi/ic_menu_account_list.png diff --git a/main/res/menu/main_activity_options.xml b/main/res/menu/main_activity_options.xml index e06e948..68fd227 100644 --- a/main/res/menu/main_activity_options.xml +++ b/main/res/menu/main_activity_options.xml @@ -12,6 +12,11 @@ android:title="@string/menu_history">
</item>
<item
+ android:id="@+id/menu_pocket_queries"
+ android:icon="@drawable/ic_menu_account_list"
+ android:title="@string/menu_pocket_queries">
+ </item>
+ <item
android:id="@+id/menu_helpers"
android:icon="@drawable/ic_menu_shopping"
android:title="@string/menu_helpers">
diff --git a/main/res/values-de/strings.xml b/main/res/values-de/strings.xml index f4a361f..78af574 100644 --- a/main/res/values-de/strings.xml +++ b/main/res/values-de/strings.xml @@ -124,6 +124,7 @@ <string name="err_start">Kommunikation nicht gestartet</string> <string name="err_parse">Parsing der Anmeldung gescheitert</string> <string name="err_server">Verbindung zu Geocaching.com konnte nicht hergestellt werden (Server oder Verbindung inaktiv?)</string> + <string name="err_server_ec">Verbindung zu Extremcaching.com konnte nicht hergestellt werden (Server oder Verbindung inaktiv?)</string> <string name="err_login">Keine Anmeldedaten gespeichert.</string> <string name="err_login_failed">Login fehlgeschlagen.</string> <string name="err_login_failed_toast">c:geo konnte sich nicht einloggen und arbeitet im Offline-Modus. Bitte die Login-Daten in den Einstellungen überprüfen oder eine Internetverbindung herstellen.</string> @@ -179,6 +180,7 @@ <string name="err_log_load_data_still">c:geo lädt gerade die benötigten Daten. Bitte kurz warten.</string> <string name="err_log_failed_server">c:geo konnte Log nicht senden, weil der Server nicht antwortete.</string> <string name="err_log_post_failed">c:geo konnte Log nicht absenden.</string> + <string name="err_log_post_failed_ec">Es scheint dass Ihr Log nicht hochgeladen werden konnte. Bitte prüfen Sie es auf Extremcaching.com nach.</string> <string name="err_logimage_post_failed">Es scheint dass Ihr Logfoto nicht hochgeladen werden konnte. Bitte prüfen Sie es auf Geocaching.com nach.</string> <string name="err_search_address_forgot">c:geo hat die Adresse vergessen, die gesucht wurde.</string> <string name="err_parse_lat">c:geo konnte den Breitengrad nicht verarbeiten.</string> @@ -343,7 +345,9 @@ <string name="settings_category_logging_other">Andere Log-Optionen</string> <string name="settings_goto_url_button">mehr …</string> <string name="settings_title_gc">Geocaching.com</string> + <string name="settings_title_ec">Extremcaching.com</string> <string name="settings_activate_gc">Aktivieren</string> + <string name="settings_activate_ec">Aktivieren</string> <string name="settings_gc_legal_note">Mit Benutzung der Dienste von geocaching.com werden die Groundspeak-Nutzungsbedingungen (englisch) akzeptiert.</string> <string name="settings_info_facebook_login_title">Facebook-Login</string> <string name="settings_info_facebook_login">c:geo kann sich zwar nicht per Facebook bei Geocaching.com einloggen, aber es gibt eine einfache Abhilfe …</string> diff --git a/main/res/values/.gitignore b/main/res/values/.gitignore index ade2b6e..891c98f 100644 --- a/main/res/values/.gitignore +++ b/main/res/values/.gitignore @@ -1,2 +1,3 @@ /ocde_okapi.xml /ocpl_okapi.xml +/keys.xml
\ No newline at end of file diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml index 5eaa6a0..ab1db9f 100644 --- a/main/res/values/attrs.xml +++ b/main/res/values/attrs.xml @@ -46,6 +46,7 @@ <attr name="text" format="string" /> <attr name="url" format="string" /> <attr name="urlButton" format="string" /> + <attr name="connector" format="string" /> <!-- others --> <attr name="compass" format="integer" /> diff --git a/main/res/values/changelog_master.xml b/main/res/values/changelog_master.xml index dae8126..5c4e88f 100644 --- a/main/res/values/changelog_master.xml +++ b/main/res/values/changelog_master.xml @@ -4,7 +4,8 @@ <string name="changelog_master" translatable="false"> <b>Next feature release</b>\n <b>New Features:</b>\n - · Browse pocket queries online (long press on nearby)\n + · Browse pocket queries online (Press menu on mainscreen)\n + · Support of extremcaching.com\n · Popup on map shows date of event caches\n · Show cache where a Geokrety is currently placed\n · Configurable twitter templates\n @@ -17,6 +18,7 @@ · Support TB URLs for the QR-code scanner\n \n <b>Bugfixes:</b>\n + · Reduce memory usage for OSM mapsforge to avoid crashes\n · Cache type filter was not applied for OC nearby search\n · No cache coords if cache is at equator or prime meridian\n · Parts of the compass not shown when using big fonts\n diff --git a/main/res/values/ecicons.xml b/main/res/values/ecicons.xml new file mode 100644 index 0000000..234db5f --- /dev/null +++ b/main/res/values/ecicons.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string-array name="ECIcons"> + <item>@string/settings_ec_icons_other</item> + <item>@string/settings_ec_icons_oc</item> + </string-array> + <string-array name="ECIconsValues"> + <item>1</item> + <item>2</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/main/res/values/ids.xml b/main/res/values/ids.xml index 4e02973..8539275 100644 --- a/main/res/values/ids.xml +++ b/main/res/values/ids.xml @@ -23,5 +23,6 @@ <item name="cache_app_download_static_maps" type="id"/> <item name="cache_app_show_static_maps" type="id"/> <item name="cache_app_locus" type="id"/> + <item name="cache_app_pebble" type="id"/> </resources>
\ No newline at end of file diff --git a/main/res/values/preference_keys.xml b/main/res/values/preference_keys.xml index 975cff9..0e4675d 100644 --- a/main/res/values/preference_keys.xml +++ b/main/res/values/preference_keys.xml @@ -18,6 +18,10 @@ <string name="pref_password">password</string> <string name="pref_connectorOCActive">connectorOCActive</string> <string name="pref_connectorOCPLActive">connectorOCPLActive</string> + <string name="pref_ecusername">ecusername</string> + <string name="pref_ecpassword">ecpassword</string> + <string name="pref_connectorECActive">connectorECActive</string> + <string name="pref_connectorOXActive">connectorOXActive</string> <string name="pref_pass_vote">pass-vote</string> <string name="pref_twitter">twitter</string> <string name="pref_webDeviceName">webDeviceName</string> @@ -116,6 +120,7 @@ <string name="pref_navigation_menu_cache_beacon">navigationCacheBeacon</string> <string name="pref_navigation_menu_gcc">navigationGcc</string> <string name="pref_navigation_menu_where_you_go">navigationWhereYouGo</string> + <string name="pref_navigation_menu_pebble">navigationPebble</string> <string name="pref_ocpl_tokensecret">ocpl_tokensecret</string> <string name="pref_ocpl_tokenpublic">ocpl_tokenpublic</string> <string name="pref_temp_ocpl_token_secret">ocpl-temp-token-secret</string> @@ -123,9 +128,12 @@ <string name="pref_fakekey_gc_website">fakekey_gc_website</string> <string name="pref_fakekey_ocde_website">fakekey_ocde_website</string> <string name="pref_fakekey_ocpl_website">fakekey_ocpl_website</string> + <string name="pref_fakekey_ec_website">fakekey_ec_website</string> + <string name="pref_fakekey_ox_website">fakekey_ox_website</string> <string name="pref_fakekey_gcvote_website">fakekey_gcvote_website</string> <string name="pref_fakekey_sendtocgeo_website">fakekey_sendtocgeo_website</string> <string name="pref_twitter_cache_message">twitter_cache_message</string> <string name="pref_twitter_trackable_message">twitter_trackable_message</string> + <string name="pref_ec_icons">ec_icons</string> </resources>
\ No newline at end of file diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml index 817ff77..385bec3 100644 --- a/main/res/values/strings.xml +++ b/main/res/values/strings.xml @@ -148,6 +148,7 @@ <string name="err_start">Communication not started</string> <string name="err_parse">Failed Login page parsing</string> <string name="err_server">Unable to contact Geocaching.com. The website may be down or your internet connection not working.</string> + <string name="err_server_ec">Unable to contact Extremcaching.com. The website may be down or your internet connection not working.</string> <string name="err_login">No Login information stored</string> <string name="err_login_failed">c:geo can\'t log in.</string> <string name="err_login_failed_toast">c:geo can\'t log in. c:geo works offline with Stored caches. Check Login settings or enable your internet connection.</string> @@ -203,6 +204,7 @@ <string name="err_log_load_data_still">c:geo is still loading data required to post log. Please wait a little while longer.</string> <string name="err_log_failed_server">c:geo failed to post log because server is not responding.</string> <string name="err_log_post_failed">It seems that your log was not posted. Please check it on Geocaching.com.</string> + <string name="err_log_post_failed_ec">It seems that your log was not posted. Please check it on Extremcaching.com.</string> <string name="err_logimage_post_failed">It seems that your log image was not uploaded. Please check it on Geocaching.com.</string> <string name="err_search_address_forgot">c:geo forgot the address you tried to find.</string> <string name="err_parse_lat">c:geo can\'t parse latitude.</string> @@ -252,7 +254,9 @@ <string name="menu_history">History</string> <string name="menu_filter">Filter</string> <string name="menu_scan_geo">Scan geocode</string> - + <string name="menu_pocket_queries">Pocket queries</string> + <string name="menu_scan_description">c:geo can scan geocodes which are printed as QR code. The necessary app is not installed. Do you want to open Google Play to install it?</string> + <!-- main screen --> <string name="live_map_button">Live map</string> <string name="caches_nearby_button">Nearby</string> @@ -391,7 +395,11 @@ <string name="settings_goto_url_button">more …</string> <string name="settings_title_gc">Geocaching.com</string> + <string name="settings_title_ec">Extremcaching.com</string> + <string name="settings_title_ox">Opencaching.com (Garmin)</string> <string name="settings_activate_gc">Activate</string> + <string name="settings_activate_ec">Activate</string> + <string name="settings_activate_ox">Activate</string> <string name="settings_gc_legal_note">With using the service of geocaching.com, you accept the Groundspeak Terms of Use.</string> <string name="settings_info_facebook_login_title">Facebook Login</string> <string name="settings_info_facebook_login">You can\'t make c:geo login to geocaching.com with your Facebook account. But there is a simple workaround …</string> @@ -523,6 +531,21 @@ <string name="settings_information">Information</string> <string name="settings_twitter_cache_message">Message for found cache</string> <string name="settings_twitter_trackable_message">Message for found trackable</string> + <string name="init_ec_icons">Map Icons</string> + <string name="settings_ec_icons_other">Other</string> + <string name="settings_ec_icons_oc">OC</string> + <string name="settings_features">Supported Features</string> + <string name="feature_description">The following <b>online</b> features of this website are supported in c:geo (in addition to the offline features):</string> + <string name="feature_personal_notes">Personal notes</string> + <string name="feature_online_logging">Online logging</string> + <string name="feature_log_images">Attach images to logs</string> + <string name="feature_watch_list">Watch list</string> + <string name="feature_own_coordinates">Storing of modified coordinates</string> + <string name="feature_search_user">Search by owner/finder</string> + <string name="feature_search_keyword">Search by keyword</string> + <string name="feature_search_live_map">Live map</string> + <string name="feature_search_center">Search by position</string> + <string name="feature_search_geocode">Search by geocode</string> <!-- map sources --> <string name="map_source_google_map">Google: Map</string> @@ -668,6 +691,7 @@ <string name="cache_menu_oruxmaps">OruxMaps</string> <string name="cache_menu_cachebeacon">Cache Beacon</string> <string name="cache_menu_navigon">Navigon</string> + <string name="cache_menu_pebble">Pebble</string> <string name="cache_status">Status</string> <string name="cache_status_offline_log">Saved Log</string> <string name="cache_status_found">Found</string> @@ -912,6 +936,8 @@ <string name="helper_calendar_title">c:geo calendar add-on</string> <string name="helper_calendar_missing">c:geo calendar add-on not installed.</string> <string name="helper_calendar_description">Enables you to export event caches to the calendar on your device.</string> + <string name="helper_sendtocgeo_title">Send to c:geo</string> + <string name="helper_sendtocgeo_description">Send to c:geo is a browser extension <strong>for your PC</strong>. When browsing geocaching.com, you can send caches to your smartphone with the click of a button directly inside the browser.</string> <string name="helper_locus_title">Locus</string> <string name="helper_locus_description">Simple, usable application which shows online maps and allows you to download them directly into Offline mode (raster maps only). Also supports track recording, POI handling and many other useful functions.</string> <string name="helper_gpsstatus_title">GPS Status</string> @@ -1137,6 +1163,14 @@ <string name="attribute_unknown_no">No unknown attribute present</string> <string name="attribute_geotour_yes">Part of GeoTour</string> <string name="attribute_geotour_no">Not part of GeoTour</string> + <string name="attribute_kids_2_yes">Take your children</string> + <string name="attribute_kids_2_no">Do not take your children</string> + <string name="attribute_historic_site_yes">Historic site</string> + <string name="attribute_historic_site_no">No historic site</string> + <string name="attribute_magnetic_yes">Magnetic cache</string> + <string name="attribute_magnetic_no">No magnetic cache</string> + <string name="attribute_usb_cache_yes">Dead drop USB cache</string> + <string name="attribute_usb_cache_no">No dead drop USB cache</string> <!-- next things --> <string name="quote">To make geocaching easier, to make users lazier.</string> diff --git a/main/res/values/strings_not_translatable.xml b/main/res/values/strings_not_translatable.xml index e5db27c..87cc291 100644 --- a/main/res/values/strings_not_translatable.xml +++ b/main/res/values/strings_not_translatable.xml @@ -90,7 +90,8 @@ · zenobios (code, loc. DE EN)\n \n · <a href="http://code.google.com/p/mapsforge/">Mapsforge</a> (OSM-rendering)\n - · <a href="http://thenounproject.com/">The Noun Project</a> (basis for attribute icons)\n + · <a href="http://thenounproject.com/">The Noun Project</a> (basis for attribute icons):\n +    · USB by Kenneth Von Alt from The Noun Project\n · <a href="http://commons.apache.org/">The Apache Commons Project</a>\n · <a href="http://androidicons.com/">Android Icons</a> (<a href="https://creativecommons.org/licenses/by/3.0/">CC-BY 3.0</a>)\n · <a href="http://rrze-icon-set.berlios.de/index.html">RRZE Icon set</a> (<a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a>)\n diff --git a/main/res/xml/preferences.xml b/main/res/xml/preferences.xml index 74a6f6e..a173601 100644 --- a/main/res/xml/preferences.xml +++ b/main/res/xml/preferences.xml @@ -59,6 +59,10 @@ </PreferenceScreen> </PreferenceCategory> <PreferenceCategory android:title="@string/settings_information" > + <cgeo.geocaching.settings.CapabilitiesPreference + android:title="@string/settings_features" + cgeo:connector="GC" /> + <cgeo.geocaching.settings.InfoPreference android:text="@string/settings_info_facebook_login" android:title="@string/settings_info_facebook_login_title" @@ -87,6 +91,9 @@ android:key="@string/pref_fakekey_ocde_authorization" /> </PreferenceCategory> <PreferenceCategory android:title="@string/settings_information" > + <cgeo.geocaching.settings.CapabilitiesPreference + android:title="@string/settings_features" + cgeo:connector="OC" /> <Preference android:key="@string/pref_fakekey_ocde_website" android:title="@string/settings_open_website" /> @@ -109,11 +116,81 @@ android:key="@string/pref_fakekey_ocpl_authorization" /> </PreferenceCategory> <PreferenceCategory android:title="@string/settings_information" > + <cgeo.geocaching.settings.CapabilitiesPreference + android:title="@string/settings_features" + cgeo:connector="OP" /> <Preference android:key="@string/pref_fakekey_ocpl_website" android:title="@string/settings_open_website" /> </PreferenceCategory> </PreferenceScreen> + + <PreferenceScreen android:title="@string/settings_title_ec" > + <PreferenceCategory android:title="@string/settings_settings" > + <CheckBoxPreference + android:defaultValue="false" + android:key="@string/pref_connectorECActive" + android:title="@string/settings_activate_ec" /> + + <EditTextPreference + android:dependency="@string/pref_connectorECActive" + android:dialogTitle="@string/init_username" + android:hint="@string/init_username" + android:imeOptions="actionDone" + android:key="@string/pref_ecusername" + android:singleLine="true" + android:title="@string/init_username" /> + + <cgeo.geocaching.settings.EditPasswordPreference + android:dependency="@string/pref_connectorECActive" + android:dialogTitle="@string/init_password" + android:hint="@string/init_password" + android:imeOptions="actionDone" + android:inputType="textPassword" + android:key="@string/pref_ecpassword" + android:singleLine="true" + android:title="@string/init_password" /> + <cgeo.geocaching.settings.CheckECCredentialsPreference + android:dependency="@string/pref_connectorECActive" + android:title="@string/init_login" /> + </PreferenceCategory> + <PreferenceCategory android:title="@string/settings_title_appearance" > + <ListPreference + android:dependency="@string/pref_connectorECActive" + android:defaultValue="1" + android:dialogTitle="@string/init_ec_icons" + android:key="@string/pref_ec_icons" + android:title="@string/init_ec_icons" + android:entries="@array/ECIcons" + android:entryValues="@array/ECIconsValues" /> + </PreferenceCategory> + <PreferenceCategory android:title="@string/settings_information" > + <cgeo.geocaching.settings.CapabilitiesPreference + android:title="@string/settings_features" + cgeo:connector="EC" /> + <Preference + android:key="@string/pref_fakekey_ec_website" + android:title="@string/settings_open_website" /> + </PreferenceCategory> + </PreferenceScreen> + + <PreferenceScreen android:title="@string/settings_title_ox" > + <PreferenceCategory android:title="@string/settings_settings" > + <CheckBoxPreference + android:defaultValue="false" + android:key="@string/pref_connectorOXActive" + android:title="@string/settings_activate_ox" /> + </PreferenceCategory> + <PreferenceCategory android:title="@string/settings_information" > + <cgeo.geocaching.settings.CapabilitiesPreference + android:title="@string/settings_features" + cgeo:connector="OX" /> + <Preference + android:key="@string/pref_fakekey_ox_website" + android:title="@string/settings_open_website" /> + </PreferenceCategory> + </PreferenceScreen> + <PreferenceScreen android:title="@string/init_gcvote" > <PreferenceCategory android:title="@string/settings_settings" > <cgeo.geocaching.settings.EditPasswordPreference diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java index 5b9b509..5f24030 100644 --- a/main/src/cgeo/geocaching/AbstractPopupActivity.java +++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java @@ -108,9 +108,6 @@ public abstract class AbstractPopupActivity extends AbstractActivity implements } @Override - public abstract void navigateTo(); - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set theme @@ -202,9 +199,6 @@ public abstract class AbstractPopupActivity extends AbstractActivity implements return super.onTouchEvent(event); } - @Override - public abstract void showNavigationMenu(); - protected abstract void startDefaultNavigation2(); protected final void addCacheDetails() { diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index b853949..079562e 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -35,6 +35,7 @@ import cgeo.geocaching.ui.IndexOutOfBoundsAvoidingTextView; import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.ui.OwnerActionsClickListener; import cgeo.geocaching.ui.WeakReferenceHandler; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.ui.logs.CacheLogsViewCreator; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.ClipboardUtils; @@ -382,7 +383,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc buildDetailsContextMenu(menu, res.getString(R.string.cache_logs), false); break; case R.id.waypoint: - menu.setHeaderTitle(res.getString(R.string.waypoint)); + menu.setHeaderTitle(selectedWaypoint.getName() + " (" + res.getString(R.string.waypoint) + ")"); getMenuInflater().inflate(R.menu.waypoint_options, menu); final boolean isOriginalWaypoint = selectedWaypoint.getWaypointType().equals(WaypointType.ORIGINAL); menu.findItem(R.id.menu_waypoint_reset_cache_coords).setVisible(isOriginalWaypoint); @@ -1615,18 +1616,8 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private void warnPersonalNoteNeedsStoring() { - final AlertDialog.Builder builder = new AlertDialog.Builder(CacheDetailActivity.this); - builder.setTitle(R.string.cache_personal_note_unstored); - builder.setMessage(R.string.cache_personal_note_store); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - // do nothing - } - }); - - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + Dialogs.confirm(CacheDetailActivity.this, R.string.cache_personal_note_unstored, R.string.cache_personal_note_store, + new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -1635,36 +1626,19 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } }); - final AlertDialog dialog = builder.create(); - dialog.setOwnerActivity(CacheDetailActivity.this); - dialog.show(); } private void warnPersonalNoteExceedsLimit() { - final AlertDialog.Builder builder = new AlertDialog.Builder(CacheDetailActivity.this); - builder.setTitle(R.string.cache_personal_note_limit); - String lang = getString(R.string.cache_personal_note_truncation, GCConstants.PERSONAL_NOTE_MAX_CHARS); - builder.setMessage(lang); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - // do nothing - } - }); + Dialogs.confirm(CacheDetailActivity.this, R.string.cache_personal_note_limit, getString(R.string.cache_personal_note_truncation, GCConstants.PERSONAL_NOTE_MAX_CHARS), + new DialogInterface.OnClickListener() { - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - uploadPersonalNote(); - } + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + uploadPersonalNote(); + } - }); - final AlertDialog dialog = builder.create(); - dialog.setOwnerActivity(CacheDetailActivity.this); - dialog.show(); + }); } } diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java index 8226f38..08d41d0 100644 --- a/main/src/cgeo/geocaching/CacheListActivity.java +++ b/main/src/cgeo/geocaching/CacheListActivity.java @@ -39,6 +39,7 @@ import cgeo.geocaching.sorting.ComparatorUserInterface; import cgeo.geocaching.ui.CacheListAdapter; import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.ui.WeakReferenceHandler; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.AsyncTaskWithProgress; import cgeo.geocaching.utils.DateUtils; import cgeo.geocaching.utils.GeoDirHandler; @@ -474,7 +475,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA // refresh standard list if it has changed (new caches downloaded) if (type == CacheListType.OFFLINE && listId >= StoredList.STANDARD_LIST_ID && search != null) { final SearchResult newSearch = DataStore.getBatchOfStoredCaches(coords, Settings.getCacheType(), listId); - if (newSearch.getTotal() != search.getTotal()) { + if (newSearch.getTotalCountGC() != search.getTotalCountGC()) { refreshCurrentList(); } } @@ -944,7 +945,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA boolean enableMore = (type != CacheListType.OFFLINE && cacheList.size() < MAX_LIST_ITEMS); if (enableMore && search != null) { - final int count = search.getTotal(); + final int count = search.getTotalCountGC(); enableMore = enableMore && count > 0 && cacheList.size() < count; } @@ -1027,27 +1028,15 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } public void removeFromHistoryCheck() { - final AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setCancelable(true); - dialog.setTitle(res.getString(R.string.caches_removing_from_history)); - dialog.setMessage((adapter != null && adapter.getCheckedCount() > 0) ? res.getString(R.string.cache_remove_from_history) - : res.getString(R.string.cache_clear_history)); - dialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + int message = (adapter != null && adapter.getCheckedCount() > 0) ? R.string.cache_remove_from_history + : R.string.cache_clear_history; + Dialogs.confirmYesNo(this, R.string.caches_removing_from_history, message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { removeFromHistory(); dialog.cancel(); } }); - dialog.setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - - final AlertDialog alert = dialog.create(); - alert.show(); } public void removeFromHistory() { @@ -1072,16 +1061,8 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } public void dropStored(final boolean removeListAfterwards) { - final AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setCancelable(true); - dialog.setTitle(res.getString(R.string.caches_drop_stored)); - - if (adapter.getCheckedCount() > 0) { - dialog.setMessage(res.getString(R.string.caches_drop_selected_ask)); - } else { - dialog.setMessage(res.getString(R.string.caches_drop_all_ask)); - } - dialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + int message = (adapter.getCheckedCount() > 0) ? R.string.caches_drop_selected_ask : R.string.caches_drop_all_ask; + Dialogs.confirmYesNo(this, R.string.caches_drop_stored, message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { @@ -1089,16 +1070,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA dialog.cancel(); } }); - dialog.setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - - final AlertDialog alert = dialog.create(); - alert.show(); } public void dropSelected(boolean removeListAfterwards) { @@ -1407,24 +1378,12 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } // ask him, if there are caches on the list - final AlertDialog.Builder alert = new AlertDialog.Builder(this); - - alert.setTitle(R.string.list_dialog_remove_title); - alert.setMessage(R.string.list_dialog_remove_description); - alert.setPositiveButton(R.string.list_dialog_remove, new DialogInterface.OnClickListener() { + Dialogs.confirm(this, R.string.list_dialog_remove_title, R.string.list_dialog_remove_description, R.string.list_dialog_remove, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { removeListInternal(); } }); - alert.setNegativeButton(res.getString(R.string.list_dialog_cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int whichButton) { - dialog.dismiss(); - } - }); - - alert.show(); } /** @@ -1631,7 +1590,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } if (coords != null) { loader = new CoordsGeocacheListLoader(app, coords); - } + } else { loader = new AddressGeocacheListLoader(app, address); } diff --git a/main/src/cgeo/geocaching/CacheMenuHandler.java b/main/src/cgeo/geocaching/CacheMenuHandler.java index 887f6cf..84a08f5 100644 --- a/main/src/cgeo/geocaching/CacheMenuHandler.java +++ b/main/src/cgeo/geocaching/CacheMenuHandler.java @@ -5,12 +5,12 @@ import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.AbstractUIFactory; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.ProcessUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; @@ -101,28 +101,17 @@ public class CacheMenuHandler extends AbstractUIFactory { Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST + "?" + params.toString()))); } else { // Inform user the calendar add-on is not installed and let them get it from Google Play - new AlertDialog.Builder(activity) - .setTitle(res.getString(R.string.addon_missing_title)) - .setMessage(new StringBuilder(res.getString(R.string.helper_calendar_missing)) - .append(' ') - .append(res.getString(R.string.addon_download_prompt)) - .toString()) - .setPositiveButton(activity.getString(android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(ICalendar.CALENDAR_ADDON_URI)); - activity.startActivity(intent); - } - }) - .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .create() - .show(); + Dialogs.confirmYesNo(activity, R.string.addon_missing_title, new StringBuilder(res.getString(R.string.helper_calendar_missing)) + .append(' ') + .append(res.getString(R.string.addon_download_prompt)) + .toString(), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(ICalendar.CALENDAR_ADDON_URI)); + activity.startActivity(intent); + } + }); } } diff --git a/main/src/cgeo/geocaching/CachePopup.java b/main/src/cgeo/geocaching/CachePopup.java index d88000f..9186497 100644 --- a/main/src/cgeo/geocaching/CachePopup.java +++ b/main/src/cgeo/geocaching/CachePopup.java @@ -26,6 +26,12 @@ public class CachePopup extends AbstractPopupActivity { private final Progress progress = new Progress(); private class StoreCacheHandler extends CancellableHandler { + private final int progressMessage; + + public StoreCacheHandler(final int progressMessage) { + this.progressMessage = progressMessage; + } + @Override public void handleRegularMessage(Message msg) { if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { @@ -36,7 +42,7 @@ public class CachePopup extends AbstractPopupActivity { } private void updateStatusMsg(final String msg) { - progress.setMessage(res.getString(R.string.cache_dialog_offline_save_message) + progress.setMessage(res.getString(progressMessage) + "\n\n" + msg); } @@ -49,23 +55,6 @@ public class CachePopup extends AbstractPopupActivity { } } - private class RefreshCacheHandler extends CancellableHandler { - @Override - public void handleRegularMessage(Message msg) { - if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { - updateStatusMsg((String) msg.obj); - } else { - init(); - } - } - - private void updateStatusMsg(final String msg) { - progress.setMessage(res.getString(R.string.cache_dialog_refresh_message) - + "\n\n" - + msg); - } - } - public CachePopup() { super(R.layout.popup); } @@ -133,7 +122,7 @@ public class CachePopup extends AbstractPopupActivity { } protected void storeCache(final int listId) { - final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(); + final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(R.string.cache_dialog_offline_save_message); progress.show(CachePopup.this, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true, storeCacheHandler.cancelMessage()); new StoreCacheThread(listId, storeCacheHandler).start(); } @@ -168,7 +157,7 @@ public class CachePopup extends AbstractPopupActivity { return; } - final RefreshCacheHandler refreshCacheHandler = new RefreshCacheHandler(); + final StoreCacheHandler refreshCacheHandler = new StoreCacheHandler(R.string.cache_dialog_offline_save_message); progress.show(CachePopup.this, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true, refreshCacheHandler.cancelMessage()); new RefreshCacheThread(refreshCacheHandler).start(); } diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index 0af8117..3d2f758 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -1,7 +1,7 @@ package cgeo.geocaching; -import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.network.StatusUpdater; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.IObserver; import cgeo.geocaching.utils.Log; @@ -75,7 +75,7 @@ public class CgeoApplication extends Application { dialog.dismiss(); boolean success = atomic.get(); String message = success ? res.getString(R.string.init_dbmove_success) : res.getString(R.string.init_dbmove_failed); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_dbmove_dbmove), message); + Dialogs.message(fromActivity, R.string.init_dbmove_dbmove, message); } }); } diff --git a/main/src/cgeo/geocaching/DirectionProvider.java b/main/src/cgeo/geocaching/DirectionProvider.java index b4fb86c..ae58fed 100644 --- a/main/src/cgeo/geocaching/DirectionProvider.java +++ b/main/src/cgeo/geocaching/DirectionProvider.java @@ -3,6 +3,8 @@ package cgeo.geocaching; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.utils.MemorySubject; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import android.app.Activity; import android.content.Context; import android.hardware.Sensor; @@ -54,6 +56,7 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve } @Override + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") public void onSensorChanged(final SensorEvent event) { final float direction = event.values[0]; if (direction != previous) { diff --git a/main/src/cgeo/geocaching/EditWaypointActivity.java b/main/src/cgeo/geocaching/EditWaypointActivity.java index c31ad40..6d0f822 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.TextUtils; @@ -331,7 +332,7 @@ public class EditWaypointActivity extends AbstractActivity { if (StringUtils.isBlank(bearingText) && StringUtils.isBlank(distanceText) && StringUtils.isBlank(latText) && StringUtils.isBlank(lonText)) { - helpDialog(res.getString(R.string.err_point_no_position_given_title), res.getString(R.string.err_point_no_position_given)); + Dialogs.message(EditWaypointActivity.this, R.string.err_point_no_position_given_title, R.string.err_point_no_position_given); return; } @@ -359,7 +360,7 @@ public class EditWaypointActivity extends AbstractActivity { try { bearing = Double.parseDouble(bearingText); } catch (NumberFormatException e) { - helpDialog(res.getString(R.string.err_point_bear_and_dist_title), res.getString(R.string.err_point_bear_and_dist)); + Dialogs.message(EditWaypointActivity.this, R.string.err_point_bear_and_dist_title, R.string.err_point_bear_and_dist); return; } diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index c589e9b..6262792 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -32,6 +32,8 @@ import cgeo.geocaching.utils.LogTemplateProvider.LogContext; import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.UncertainProperty; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.Predicate; import org.apache.commons.lang3.BooleanUtils; @@ -366,6 +368,7 @@ public class Geocache implements ICache, IWaypoint { * the other cache to compare this one to * @return true if both caches have the same content */ + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") private boolean isEqualTo(final Geocache other) { return detailed == other.detailed && StringUtils.equalsIgnoreCase(geocode, other.geocode) && @@ -1578,7 +1581,7 @@ public class Geocache implements ICache, IWaypoint { public static void storeCache(Geocache origCache, String geocode, int listId, boolean forceRedownload, CancellableHandler handler) { try { - Geocache cache; + Geocache cache = null; // get cache details, they may not yet be complete if (origCache != null) { SearchResult search = null; @@ -1593,9 +1596,9 @@ public class Geocache implements ICache, IWaypoint { } } else if (StringUtils.isNotBlank(geocode)) { final SearchResult search = searchByGeocode(geocode, null, listId, forceRedownload, handler); - cache = search.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); - } else { - cache = null; + if (search != null) { + cache = search.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); + } } if (cache == null) { @@ -1710,7 +1713,7 @@ public class Geocache implements ICache, IWaypoint { patterns.add(Pattern.compile("\\b(\\d{1,2})\\:(\\d\\d)\\b")); if (StringUtils.isNotBlank(hourLocalized)) { // 17 - 20 o'clock - patterns.add(Pattern.compile("\\b(\\d{1,2})(?:\\.00)?" + "\\s*-\\s*" + "(?:\\d{1,2})(?:\\.00)?" + "\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE)); + patterns.add(Pattern.compile("\\b(\\d{1,2})(?:\\.00)?" + "\\s*(?:-|[a-z]+)\\s*" + "(?:\\d{1,2})(?:\\.00)?" + "\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE)); // 12 o'clock, 12.00 o'clock patterns.add(Pattern.compile("\\b(\\d{1,2})(?:\\.00)?\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE)); } @@ -1719,10 +1722,10 @@ public class Geocache implements ICache, IWaypoint { final MatcherWrapper matcher = new MatcherWrapper(pattern, getDescription()); while (matcher.find()) { try { - final int hours = Integer.valueOf(matcher.group(1)); + final int hours = Integer.parseInt(matcher.group(1)); int minutes = 0; if (matcher.groupCount() >= 2) { - minutes = Integer.valueOf(matcher.group(2)); + minutes = Integer.parseInt(matcher.group(2)); } if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) { return String.valueOf(hours * 60 + minutes); @@ -1810,4 +1813,21 @@ public class Geocache implements ICache, IWaypoint { public String getWaypointPrefix(String name) { return getConnector().getWaypointPrefix(name); } + + /** + * Get number of overall finds for a cache, or 0 if the number of finds is not known. + * + * @return + */ + public int getFindsCount() { + if (getLogCounts().isEmpty()) { + setLogCounts(DataStore.loadLogCounts(getGeocode())); + } + Integer logged = getLogCounts().get(LogType.FOUND_IT); + if (logged != null) { + return logged; + } + return 0; + } + } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index e549fdc..5246fa9 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -4,7 +4,7 @@ import butterknife.ButterKnife; import butterknife.InjectView; import cgeo.geocaching.connector.gc.GCParser; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Network; @@ -76,7 +76,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat showToast(res.getString(R.string.info_log_type_changed)); } - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { if (attempts < 2) { showToast(res.getString(R.string.err_log_load_data_again)); new LoadDataThread().start(); @@ -200,7 +200,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat possibleLogTypes = Trackable.getPossibleLogTypes(); } - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { buttonPost.setEnabled(false); buttonPost.setOnTouchListener(null); buttonPost.setOnClickListener(null); @@ -294,7 +294,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat final String page = Network.getResponseData(Network.getRequest("http://www.geocaching.com/track/log.aspx", params)); - viewstates = Login.getViewstates(page); + viewstates = GCLogin.getViewstates(page); final List<LogType> typesPre = GCParser.parseTypes(page); if (CollectionUtils.isNotEmpty(typesPre)) { diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 1d4c22c..d96b97c 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -15,10 +15,10 @@ import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.ProcessUtils; import cgeo.geocaching.utils.RunnableWithArgument; import cgeo.geocaching.utils.Version; @@ -27,7 +27,6 @@ import com.google.zxing.integration.android.IntentResult; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.eclipse.jdt.annotation.NonNull; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -72,7 +71,6 @@ public class MainActivity extends AbstractActivity { @InjectView(R.id.offline_count) protected TextView countBubble; @InjectView(R.id.info_area) protected LinearLayout infoArea; - private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN"; public static final int SEARCH_REQUEST_CODE = 2; private int version = 0; @@ -268,7 +266,7 @@ public class MainActivity extends AbstractActivity { @Override public boolean onPrepareOptionsMenu(final Menu menu) { super.onPrepareOptionsMenu(menu); - menu.findItem(R.id.menu_scan).setEnabled(ProcessUtils.isIntentAvailable(SCAN_INTENT)); + menu.findItem(R.id.menu_pocket_queries).setVisible(Settings.isPremiumMember()); return true; } @@ -291,6 +289,18 @@ public class MainActivity extends AbstractActivity { case R.id.menu_scan: startScannerApplication(); return true; + case R.id.menu_pocket_queries: + if (!Settings.isPremiumMember()) { + return true; + } + new PocketQueryList.UserInterface(MainActivity.this).promptForListSelection(new RunnableWithArgument<PocketQueryList>() { + + @Override + public void run(final PocketQueryList pql) { + CacheListActivity.startActivityPocket(MainActivity.this, pql); + } + }); + return true; default: return super.onOptionsItemSelected(item); } @@ -299,6 +309,11 @@ public class MainActivity extends AbstractActivity { private void startScannerApplication() { IntentIntegrator integrator = new IntentIntegrator(this); + // integrator dialog is English only, therefore localize it + integrator.setButtonYesByID(android.R.string.yes); + integrator.setButtonNoByID(android.R.string.no); + integrator.setTitleByID(R.string.menu_scan_geo); + integrator.setMessageByID(R.string.menu_scan_description); integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES); } @@ -318,11 +333,7 @@ public class MainActivity extends AbstractActivity { if (query == null) { query = ""; } - new AlertDialog.Builder(this) - .setMessage(res.getString(R.string.unknown_scan) + "\n\n" + query) - .setPositiveButton(getString(android.R.string.ok), null) - .create() - .show(); + Dialogs.message(this, res.getString(R.string.unknown_scan) + "\n\n" + query); } } } @@ -513,26 +524,6 @@ public class MainActivity extends AbstractActivity { } }); nearestView.setBackgroundResource(R.drawable.main_nearby); - - nearestView.setOnLongClickListener(new View.OnLongClickListener() { - - @Override - public boolean onLongClick(View v) { - if (!Settings.isPremiumMember()) { - return true; - } - new PocketQueryList.UserInterface(MainActivity.this).promptForListSelection(new RunnableWithArgument<PocketQueryList>() { - - @Override - public void run(final @NonNull PocketQueryList pql) { - CacheListActivity.startActivityPocket(MainActivity.this, pql); - } - }); - return true; - } - }); - nearestView.setLongClickable(true); - } navType.setText(res.getString(geo.getLocationProvider().resourceId)); @@ -665,10 +656,10 @@ public class MainActivity extends AbstractActivity { int checks = 0; while (!DataStore.isInitialized()) { try { - wait(500); + sleep(500); checks++; } catch (Exception e) { - // nothing; + Log.e("MainActivity.CountBubbleUpdateThread.run", e); } if (checks > 10) { diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index c68c979..f24e86e 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -13,6 +13,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractViewHolder; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -518,7 +519,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { try { bearing = Double.parseDouble(bearingText); } catch (NumberFormatException e) { - helpDialog(res.getString(R.string.err_point_bear_and_dist_title), res.getString(R.string.err_point_bear_and_dist)); + Dialogs.message(this, R.string.err_point_bear_and_dist_title, R.string.err_point_bear_and_dist); return null; } diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index 334d99a..9952e18 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.EditUtils; import org.apache.commons.lang3.StringUtils; @@ -276,7 +277,7 @@ public class SearchActivity extends AbstractActivity { final String keyText = StringUtils.trim(keywordEditText.getText().toString()); if (StringUtils.isBlank(keyText)) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_keyword)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_keyword); return; } @@ -287,7 +288,7 @@ public class SearchActivity extends AbstractActivity { final String addText = StringUtils.trim(addressEditText.getText().toString()); if (StringUtils.isBlank(addText)) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_address)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_address); return; } @@ -300,7 +301,7 @@ public class SearchActivity extends AbstractActivity { final String usernameText = StringUtils.trim(userNameEditText.getText().toString()); if (StringUtils.isBlank(usernameText)) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_user)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_user); return; } @@ -315,7 +316,7 @@ public class SearchActivity extends AbstractActivity { final String usernameText = StringUtils.trimToEmpty(userName); if (StringUtils.isBlank(usernameText)) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_user)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_user); return; } @@ -326,7 +327,7 @@ public class SearchActivity extends AbstractActivity { final String geocodeText = StringUtils.trim(geocodeEditText.getText().toString()); if (StringUtils.isBlank(geocodeText) || geocodeText.equalsIgnoreCase("GC")) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_gccode)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_gccode); return; } @@ -337,7 +338,7 @@ public class SearchActivity extends AbstractActivity { final String trackableText = StringUtils.trim(trackableEditText.getText().toString()); if (StringUtils.isBlank(trackableText) || trackableText.equalsIgnoreCase("TB")) { - helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_tb)); + Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_tb); return; } diff --git a/main/src/cgeo/geocaching/SearchResult.java b/main/src/cgeo/geocaching/SearchResult.java index 0bdf6c6..5d63a2d 100644 --- a/main/src/cgeo/geocaching/SearchResult.java +++ b/main/src/cgeo/geocaching/SearchResult.java @@ -1,6 +1,6 @@ package cgeo.geocaching; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.LoadFlag; @@ -28,7 +28,11 @@ public class SearchResult implements Parcelable { private StatusCode error = null; private String url = ""; public String[] viewstates = null; - private int totalCnt = 0; + /** + * Overall number of search results matching our search on geocaching.com. If this number is higher than 20, we have + * to fetch multiple pages to get all caches. + */ + private int totalCountGC = 0; final public static Parcelable.Creator<SearchResult> CREATOR = new Parcelable.Creator<SearchResult>() { @Override @@ -60,20 +64,23 @@ public class SearchResult implements Parcelable { error = searchResult.error; url = searchResult.url; viewstates = searchResult.viewstates; - setTotal(searchResult.getTotal()); + setTotalCountGC(searchResult.getTotalCountGC()); } /** * Build a search result from an existing collection of geocodes. * - * @param geocodes a non-null collection of geocodes - * @param total the total number of geocodes (FIXME: what is the meaning of this number wrt to geocodes.size()?) + * @param geocodes + * a non-null collection of geocodes + * @param totalCountGC + * the total number of caches matching that search on geocaching.com (as we always get only the next 20 + * from a web page) */ - public SearchResult(final Collection<String> geocodes, final int total) { + public SearchResult(final Collection<String> geocodes, final int totalCountGC) { this.geocodes = new HashSet<String>(geocodes.size()); this.geocodes.addAll(geocodes); this.filteredGeocodes = new HashSet<String>(); - this.setTotal(total); + this.setTotalCountGC(totalCountGC); } /** @@ -99,7 +106,7 @@ public class SearchResult implements Parcelable { viewstates = new String[length]; in.readStringArray(viewstates); } - setTotal(in.readInt()); + setTotalCountGC(in.readInt()); } /** @@ -136,7 +143,7 @@ public class SearchResult implements Parcelable { out.writeInt(viewstates.length); out.writeStringArray(viewstates); } - out.writeInt(getTotal()); + out.writeInt(getTotalCountGC()); } @Override @@ -173,19 +180,23 @@ public class SearchResult implements Parcelable { } public void setViewstates(String[] viewstates) { - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { return; } + // lazy initialization of viewstates + if (this.viewstates == null) { + this.viewstates = new String[viewstates.length]; + } System.arraycopy(viewstates, 0, this.viewstates, 0, viewstates.length); } - public int getTotal() { - return totalCnt; + public int getTotalCountGC() { + return totalCountGC; } - public void setTotal(int totalCnt) { - this.totalCnt = totalCnt; + public void setTotalCountGC(int totalCountGC) { + this.totalCountGC = totalCountGC; } /** @@ -214,7 +225,7 @@ public class SearchResult implements Parcelable { } } // decrease maximum number of caches by filtered ones - result.setTotal(result.getTotal() - excluded); + result.setTotalCountGC(result.getTotalCountGC() - excluded); GCVote.loadRatings(cachesForVote); return result; } @@ -268,12 +279,18 @@ public class SearchResult implements Parcelable { } public void addSearchResult(SearchResult other) { - if (other != null) { - addGeocodes(other.geocodes); - addFilteredGeocodes(other.filteredGeocodes); - if (StringUtils.isBlank(url)) { - url = other.url; - } + if (other == null) { + return; + } + addGeocodes(other.geocodes); + addFilteredGeocodes(other.filteredGeocodes); + if (StringUtils.isBlank(url)) { + url = other.url; + } + // copy the GC total search results number to be able to use "More caches" button + if (getTotalCountGC() == 0 && other.getTotalCountGC() != 0) { + setViewstates(other.getViewstates()); + setTotalCountGC(other.getTotalCountGC()); } } diff --git a/main/src/cgeo/geocaching/UsefulAppsActivity.java b/main/src/cgeo/geocaching/UsefulAppsActivity.java index c0f2d6f..c70143f 100644 --- a/main/src/cgeo/geocaching/UsefulAppsActivity.java +++ b/main/src/cgeo/geocaching/UsefulAppsActivity.java @@ -10,6 +10,7 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.text.Html; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -46,7 +47,9 @@ public class UsefulAppsActivity extends AbstractActivity { private void installFromMarket(Activity activity) { try { - Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName)); + // allow also opening pure http URLs in addition to market packages + final String url = (packageName.startsWith("http:")) ? packageName : "market://details?id=" + packageName; + final Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); marketIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); activity.startActivity(marketIntent); @@ -58,6 +61,7 @@ public class UsefulAppsActivity extends AbstractActivity { private static final HelperApp[] HELPER_APPS = { new HelperApp(R.string.helper_calendar_title, R.string.helper_calendar_description, R.drawable.cgeo, "cgeo.calendar"), + new HelperApp(R.string.helper_sendtocgeo_title, R.string.helper_sendtocgeo_description, R.drawable.cgeo, "http://send2.cgeo.org"), new HelperApp(R.string.helper_pocketquery_title, R.string.helper_pocketquery_description, R.drawable.helper_pocketquery, "org.pquery"), new HelperApp(R.string.helper_locus_title, R.string.helper_locus_description, R.drawable.helper_locus, "menion.android.locus"), new HelperApp(R.string.helper_google_translate_title, R.string.helper_google_translate_description, R.drawable.helper_google_translate, "com.google.android.apps.translate"), @@ -92,7 +96,7 @@ public class UsefulAppsActivity extends AbstractActivity { private void fillViewHolder(ViewHolder holder, HelperApp app) { holder.title.setText(res.getString(app.titleId)); holder.image.setImageDrawable(res.getDrawable(app.iconId)); - holder.description.setText(res.getString(app.descriptionId)); + holder.description.setText(Html.fromHtml(res.getString(app.descriptionId))); } }); diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java index dda83d9..47977bc 100644 --- a/main/src/cgeo/geocaching/Waypoint.java +++ b/main/src/cgeo/geocaching/Waypoint.java @@ -270,7 +270,6 @@ public class Waypoint implements IWaypoint { /** * Delegates the creation of the waypoint-id for gpx-export to the waypoint * - * @param prefix * @return */ public String getGpxId() { diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 1f44536..df21a8c 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -9,7 +9,6 @@ import cgeo.geocaching.settings.Settings; import android.content.Context; import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; @@ -58,15 +57,6 @@ public abstract class AbstractActivity extends FragmentActivity implements IAbst } @Override - public final void helpDialog(final String title, final String message) { - ActivityMixin.helpDialog(this, title, message); - } - - protected final void helpDialog(final String title, final String message, final Drawable icon) { - ActivityMixin.helpDialog(this, title, message, icon); - } - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initializeCommonFields(); diff --git a/main/src/cgeo/geocaching/activity/AbstractListActivity.java b/main/src/cgeo/geocaching/activity/AbstractListActivity.java index d2bc0b4..a5d5c14 100644 --- a/main/src/cgeo/geocaching/activity/AbstractListActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractListActivity.java @@ -3,7 +3,6 @@ package cgeo.geocaching.activity; import cgeo.geocaching.CgeoApplication; import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.FragmentListActivity; import android.view.View; @@ -48,15 +47,6 @@ public abstract class AbstractListActivity extends FragmentListActivity implemen } @Override - public final void helpDialog(final String title, final String message) { - ActivityMixin.helpDialog(this, title, message, null); - } - - public final void helpDialog(final String title, final String message, final Drawable icon) { - ActivityMixin.helpDialog(this, title, message, icon); - } - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initializeCommonFields(); diff --git a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java index 952726e..049fc7d 100644 --- a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java @@ -7,8 +7,11 @@ import com.viewpagerindicator.TitlePageIndicator; import com.viewpagerindicator.TitleProvider; import org.apache.commons.lang3.tuple.Pair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.app.Activity; +import android.os.Bundle; import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; @@ -43,6 +46,10 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends private final Map<Page, PageViewCreator> viewCreators = new HashMap<Page, PageViewCreator>(); /** + * Store the states of the page views to be able to persist them when destroyed and reinstantiated again + */ + private final Map<Page, Bundle> viewStates = new HashMap<Page, Bundle>(); + /** * The {@link ViewPager} for this activity. */ private ViewPager viewPager; @@ -76,6 +83,17 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends * Handles changed data-sets. */ public void notifyDataSetChanged(); + + /** + * Gets state of the view + */ + public @Nullable + Bundle getViewState(); + + /** + * Set the state of the view + */ + public void setViewState(@NonNull Bundle state); } /** @@ -93,6 +111,19 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends @Override public void destroyItem(ViewGroup container, int position, Object object) { + + final Page page = pageOrder.get(position); + + // Store the state of the view if the page supports it + PageViewCreator creator = viewCreators.get(page); + if (creator != null) { + @Nullable + Bundle state = creator.getViewState(); + if (state != null) { + viewStates.put(page, state); + } + } + container.removeView((View) object); } @@ -107,6 +138,7 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends @Override public Object instantiateItem(ViewGroup container, int position) { + final Page page = pageOrder.get(position); PageViewCreator creator = viewCreators.get(page); @@ -114,6 +146,7 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends if (null == creator && null != page) { creator = AbstractViewPagerActivity.this.createViewCreator(page); viewCreators.put(page, creator); + viewStates.put(page, new Bundle()); } View view = null; @@ -123,12 +156,18 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends // Result from getView() is maybe cached, but it should be valid because the // creator should be informed about data-changes with notifyDataSetChanged() view = creator.getView(); + + // Restore the state of the view if the page supports it + Bundle state = viewStates.get(page); + if (state != null) { + creator.setViewState(state); + } + container.addView(view, 0); } } catch (Exception e) { Log.e("ViewPagerAdapter.instantiateItem ", e); } - return view; } @@ -225,10 +264,15 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends protected abstract String getTitle(Page page); protected final void reinitializeViewPager() { + // notify all creators that the data has changed for (PageViewCreator creator : viewCreators.values()) { creator.notifyDataSetChanged(); } + // reset the stored view states of all pages + for (Bundle state : viewStates.values()) { + state.clear(); + } pageOrder.clear(); final Pair<List<? extends Page>, Integer> pagesAndIndex = getOrderedPages(); diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index 9b1e433..c1a2678 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -8,10 +8,7 @@ import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.StringUtils; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Build; import android.view.Gravity; import android.view.View; @@ -94,30 +91,6 @@ public final class ActivityMixin { } } - public static void helpDialog(final Activity activity, final String title, final String message, final Drawable icon) { - if (StringUtils.isBlank(message)) { - return; - } - - AlertDialog.Builder dialog = new AlertDialog.Builder(activity).setTitle(title).setMessage(message).setCancelable(true); - dialog.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - if (icon != null) { - dialog.setIcon(icon); - } - - AlertDialog alert = dialog.create(); - alert.show(); - } - - public static void helpDialog(Activity activity, String title, String message) { - helpDialog(activity, title, message, null); - } - public static void keepScreenOn(final Activity abstractActivity, boolean keepScreenOn) { if (keepScreenOn) { abstractActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -130,7 +103,7 @@ public final class ActivityMixin { /** * insert text into the EditText at the current cursor position - * + * * @param editText * @param insertText * @param moveCursor diff --git a/main/src/cgeo/geocaching/activity/IAbstractActivity.java b/main/src/cgeo/geocaching/activity/IAbstractActivity.java index 61c218b..7ca2322 100644 --- a/main/src/cgeo/geocaching/activity/IAbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/IAbstractActivity.java @@ -10,7 +10,5 @@ public interface IAbstractActivity { public void showShortToast(String text); - public void helpDialog(String title, String message); - public void invalidateOptionsMenuCompatible(); } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index dd02bc1..bf0e776 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -71,7 +71,8 @@ public final class NavigationAppFactory extends AbstractAppFactory { CACHE_BEACON(new CacheBeaconApp(), 14, R.string.pref_navigation_menu_cache_beacon), GCC(new GccApp(), 15, R.string.pref_navigation_menu_gcc), - WHERE_YOU_GO(new WhereYouGoApp(), 16, R.string.pref_navigation_menu_where_you_go); + WHERE_YOU_GO(new WhereYouGoApp(), 16, R.string.pref_navigation_menu_where_you_go), + PEBBLE(new PebbleApp(), 17, R.string.pref_navigation_menu_pebble); NavigationAppsEnum(final App app, final int id, final int preferenceKey) { this.app = app; @@ -142,8 +143,6 @@ public final class NavigationAppFactory extends AbstractAppFactory { public static void showNavigationMenu(final Activity activity, final Geocache cache, final Waypoint waypoint, final Geopoint destination, final boolean showInternalMap, final boolean showDefaultNavigation) { - final AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.cache_menu_navigate); final List<NavigationAppsEnum> items = new ArrayList<NavigationAppFactory.NavigationAppsEnum>(); final int defaultNavigationTool = Settings.getDefaultNavigationTool(); for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { @@ -166,26 +165,25 @@ public final class NavigationAppFactory extends AbstractAppFactory { } } } + + if (items.size() == 1) { + invokeNavigation(activity, cache, waypoint, destination, items.get(0).app); + return; + } + /* * Using an ArrayAdapter with list of NavigationAppsEnum items avoids * handling between mapping list positions allows us to do dynamic filtering of the list based on use case. */ final ArrayAdapter<NavigationAppsEnum> adapter = new ArrayAdapter<NavigationAppsEnum>(activity, android.R.layout.select_dialog_item, items); + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.cache_menu_navigate); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { final NavigationAppsEnum selectedItem = adapter.getItem(item); - final App app = selectedItem.app; - if (cache != null) { - navigateCache(activity, cache, app); - } - else if (waypoint != null) { - navigateWaypoint(activity, waypoint, app); - } - else { - navigateGeopoint(activity, destination, app); - } + invokeNavigation(activity, cache, waypoint, destination, selectedItem.app); } }); final AlertDialog alert = builder.create(); @@ -225,7 +223,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { /** * Handles menu selections for menu entries created with * {@link #showNavigationMenu(Activity, Geocache, Waypoint, Geopoint)}. - * + * * @param item * @param activity * @param cache @@ -343,4 +341,16 @@ public final class NavigationAppFactory extends AbstractAppFactory { return NavigationAppsEnum.COMPASS.app; } + private static void invokeNavigation(final Activity activity, final Geocache cache, final Waypoint waypoint, final Geopoint destination, final App app) { + if (cache != null) { + navigateCache(activity, cache, app); + } + else if (waypoint != null) { + navigateWaypoint(activity, waypoint, app); + } + else { + navigateGeopoint(activity, destination, app); + } + } + } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/PebbleApp.java b/main/src/cgeo/geocaching/apps/cache/navi/PebbleApp.java new file mode 100644 index 0000000..8ba3bef --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cache/navi/PebbleApp.java @@ -0,0 +1,44 @@ +package cgeo.geocaching.apps.cache.navi;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.R;
+import cgeo.geocaching.geopoint.Geopoint;
+
+import android.app.Activity;
+import android.content.Intent;
+
+/**
+ * Application for communication with the Pebble watch.
+ *
+ */
+class PebbleApp extends AbstractPointNavigationApp {
+
+ private static final String INTENT = "com.webmajstr.pebble_gc.NAVIGATE_TO";
+ private static final String PACKAGE_NAME = "com.webmajstr.pebble_gc";
+
+ PebbleApp() {
+ super(getString(R.string.cache_menu_pebble), R.id.cache_app_pebble, INTENT, PACKAGE_NAME);
+ }
+
+ @Override
+ public void navigate(Activity activity, Geopoint point) {
+ final Intent pebbleIntent = new Intent(INTENT);
+ pebbleIntent.putExtra("latitude", point.getLatitude());
+ pebbleIntent.putExtra("longitude", point.getLongitude());
+ activity.startActivity(pebbleIntent);
+ }
+
+ @Override
+ public void navigate(Activity activity, Geocache cache) {
+ final Intent pebbleIntent = new Intent(INTENT);
+ pebbleIntent.putExtra("latitude", cache.getCoords().getLatitude());
+ pebbleIntent.putExtra("longitude", cache.getCoords().getLongitude());
+ pebbleIntent.putExtra("difficulty", cache.getDifficulty());
+ pebbleIntent.putExtra("terrain", cache.getTerrain());
+ pebbleIntent.putExtra("name", cache.getName());
+ pebbleIntent.putExtra("code", cache.getGeocode());
+ pebbleIntent.putExtra("size", cache.getSize().getL10n());
+ activity.startActivity(pebbleIntent);
+ }
+
+}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index b10366e..ffb1b1f 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -1,21 +1,28 @@ package cgeo.geocaching.connector; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByKeyword; +import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public abstract class AbstractConnector implements IConnector { @Override - public boolean canHandle(String geocode) { + public boolean canHandle(@NonNull final String geocode) { return false; } @@ -88,7 +95,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public String getLicenseText(final Geocache cache) { + public String getLicenseText(final @NonNull Geocache cache) { return null; } @@ -132,15 +139,12 @@ public abstract class AbstractConnector implements IConnector { abstract protected String getCacheUrlPrefix(); @Override - public String getLongCacheUrl(final Geocache cache) { + public String getLongCacheUrl(final @NonNull Geocache cache) { return getCacheUrl(cache); } - /** - * {@link IConnector} - */ @Override - public boolean isActivated() { + public boolean isActive() { return false; } @@ -200,4 +204,48 @@ public abstract class AbstractConnector implements IConnector { // Default: just return the name return name; } + + @Override + public int getMaxTerrain() { + return 5; + } + + @Override + public final Collection<String> getCapabilities() { + ArrayList<String> builder = new ArrayList<String>(); + builder.add(capability(ISearchByViewPort.class, R.string.feature_search_live_map)); + builder.add(capability(ISearchByKeyword.class, R.string.feature_search_keyword)); + builder.add(capability(ISearchByCenter.class, R.string.feature_search_center)); + builder.add(capability(ISearchByGeocode.class, R.string.feature_search_geocode)); + if (supportsUserActions()) { + builder.add(feature(R.string.feature_search_user)); + } + if (supportsLogging()) { + builder.add(feature(R.string.feature_online_logging)); + } + if (supportsLogImages()) { + builder.add(feature(R.string.feature_log_images)); + } + if (supportsPersonalNote()) { + builder.add(feature(R.string.feature_personal_notes)); + } + if (supportsOwnCoordinates()) { + builder.add(feature(R.string.feature_own_coordinates)); + } + if (supportsWatchList()) { + builder.add(feature(R.string.feature_watch_list)); + } + return builder; + } + + private String capability(Class<? extends IConnector> clazz, final int featureResourceId) { + if (clazz.isInstance(this)) { + return feature(featureResourceId); + } + return StringUtils.EMPTY; + } + + private static String feature(int featureResourceId) { + return CgeoApplication.getInstance().getString(featureResourceId); + } } diff --git a/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java new file mode 100644 index 0000000..9e702c4 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java @@ -0,0 +1,20 @@ +package cgeo.geocaching.connector; + +import cgeo.geocaching.TrackableLog; + +import java.util.Collections; +import java.util.List; + +public abstract class AbstractLoggingManager implements ILoggingManager { + + @Override + public boolean hasLoaderError() { + return false; + } + + @Override + public List<TrackableLog> getTrackables() { + return Collections.emptyList(); + } + +} diff --git a/main/src/cgeo/geocaching/connector/AbstractLogin.java b/main/src/cgeo/geocaching/connector/AbstractLogin.java new file mode 100644 index 0000000..6527685 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/AbstractLogin.java @@ -0,0 +1,77 @@ +package cgeo.geocaching.connector; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.settings.Settings; + +import org.apache.commons.lang3.StringUtils; + +public abstract class AbstractLogin { + + /** + * {@code true} if logged in, {@code false} otherwise + */ + private boolean actualLoginStatus = false; + private String actualUserName = StringUtils.EMPTY; + /** + * Number of caches found. An unknown number is signaled by the value -1, while 0 really indicates zero caches found + * by the user. + */ + private int actualCachesFound = -1; + private String actualStatus = StringUtils.EMPTY; + + public void setActualCachesFound(final int found) { + actualCachesFound = found; + } + + public String getActualStatus() { + return actualStatus; + } + + protected void setActualStatus(final String status) { + actualStatus = status; + } + + public boolean isActualLoginStatus() { + return actualLoginStatus; + } + + protected void setActualLoginStatus(boolean loginStatus) { + actualLoginStatus = loginStatus; + } + + public String getActualUserName() { + return actualUserName; + } + + protected void setActualUserName(String userName) { + actualUserName = userName; + } + + public int getActualCachesFound() { + return actualCachesFound; + } + + protected void resetLoginStatus() { + Cookies.clearCookies(); + Settings.setCookieStore(null); + + setActualLoginStatus(false); + } + + protected void clearLoginInfo() { + resetLoginStatus(); + + setActualCachesFound(-1); + setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); + } + + public StatusCode login() { + return login(true); + } + + protected abstract StatusCode login(boolean retry); + +} diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 83f8142..3df98e0 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -8,7 +8,9 @@ import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.connector.ec.ECConnector; import cgeo.geocaching.connector.gc.GCConnector; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.connector.oc.OCApiConnector; import cgeo.geocaching.connector.oc.OCApiConnector.ApiSupport; import cgeo.geocaching.connector.oc.OCApiLiveConnector; @@ -24,12 +26,16 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; public final class ConnectorFactory { private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); - private static final IConnector[] CONNECTORS = new IConnector[] { + private static final Collection<IConnector> CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new IConnector[] { GCConnector.getInstance(), + ECConnector.getInstance(), new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", "CC BY-NC-ND, alle Logeinträge © jeweiliger Autor", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret, R.string.pref_connectorOCActive, R.string.pref_ocde_tokenpublic, R.string.pref_ocde_tokensecret, ApiSupport.current), @@ -43,71 +49,54 @@ public final class ConnectorFactory { new OCApiLiveConnector("opencaching.pl", "www.opencaching.pl", "OP", "CC BY-SA 3.0", R.string.oc_pl_okapi_consumer_key, R.string.oc_pl_okapi_consumer_secret, R.string.pref_connectorOCPLActive, R.string.pref_ocpl_tokenpublic, R.string.pref_ocpl_tokensecret, ApiSupport.current), - new OCApiConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA", "CC BY-NC-SA 2.5", ApiSupport.oldapi), + new OCApiConnector("OpenCaching.US", "www.opencaching.us", "OU", "pTsYAYSXFcfcRQnYE6uA", "CC BY-NC-SA 2.5", ApiSupport.current), new OXConnector(), new GeocachingAustraliaConnector(), new GeopeitusConnector(), new WaymarkingConnector(), UNKNOWN_CONNECTOR // the unknown connector MUST be the last one - }; + })); @NonNull public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector(); - private static final TrackableConnector[] TRACKABLE_CONNECTORS = new TrackableConnector[] { + private static final Collection<TrackableConnector> TRACKABLE_CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new TrackableConnector[] { new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs TravelBugConnector.getInstance(), UNKNOWN_TRACKABLE_CONNECTOR // must be last - }; + })); - private static final ISearchByViewPort[] searchByViewPortConns; + private static final Collection<ISearchByViewPort> searchByViewPortConns = getMatchingConnectors(ISearchByViewPort.class); - private static final ISearchByCenter[] searchByCenterConns; + private static final Collection<ISearchByCenter> searchByCenterConns = getMatchingConnectors(ISearchByCenter.class); - private static final ISearchByKeyword[] searchByKeywordConns; + private static final Collection<ISearchByKeyword> searchByKeywordConns = getMatchingConnectors(ISearchByKeyword.class); - static { - final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); - for (final IConnector conn : CONNECTORS) { - if (conn instanceof ISearchByViewPort) { - vpConns.add((ISearchByViewPort) conn); - } - } - searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[vpConns.size()]); - - final List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>(); - for (final IConnector conn : CONNECTORS) { - // GCConnector is handled specially, omit it here! - if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) { - centerConns.add((ISearchByCenter) conn); - } - } - searchByCenterConns = centerConns.toArray(new ISearchByCenter[centerConns.size()]); - - final List<ISearchByKeyword> keywordConns = new ArrayList<ISearchByKeyword>(); - for (final IConnector conn : CONNECTORS) { - // GCConnector is handled specially, omit it here! - if (conn instanceof ISearchByKeyword && !(conn instanceof GCConnector)) { - keywordConns.add((ISearchByKeyword) conn); + @SuppressWarnings("unchecked") + private static <T extends IConnector> Collection<T> getMatchingConnectors(final Class<T> clazz) { + final List<T> matching = new ArrayList<T>(); + for (final IConnector connector : CONNECTORS) { + if (clazz.isInstance(connector)) { + matching.add((T) connector); } } - searchByKeywordConns = keywordConns.toArray(new ISearchByKeyword[keywordConns.size()]); + return Collections.unmodifiableCollection(matching); } - public static IConnector[] getConnectors() { + public static Collection<IConnector> getConnectors() { return CONNECTORS; } - public static ISearchByCenter[] getSearchByCenterConnectors() { + public static Collection<ISearchByCenter> getSearchByCenterConnectors() { return searchByCenterConns; } - public static ISearchByKeyword[] getSearchByKeywordConnectors() { + public static Collection<ISearchByKeyword> getSearchByKeywordConnectors() { return searchByKeywordConns; } public static ILogin[] getActiveLiveConnectors() { final List<ILogin> liveConns = new ArrayList<ILogin>(); for (final IConnector conn : CONNECTORS) { - if (conn instanceof ILogin && conn.isActivated()) { + if (conn instanceof ILogin && conn.isActive()) { liveConns.add((ILogin) conn); } } @@ -147,6 +136,9 @@ public final class ConnectorFactory { public static IConnector getConnector(final String geocodeInput) { // this may come from user input final String geocode = StringUtils.trim(geocodeInput); + if (geocode == null) { + return UNKNOWN_CONNECTOR; + } if (isInvalidGeocode(geocode)) { return UNKNOWN_CONNECTOR; } @@ -164,12 +156,11 @@ public final class ConnectorFactory { } /** @see ISearchByViewPort#searchByViewport */ - public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - + public static SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens) { final SearchResult result = new SearchResult(); - for (final ISearchByViewPort vpconn : searchByViewPortConns) { - if (vpconn.isActivated()) { - result.addSearchResult(vpconn.searchByViewport(viewport, tokens)); + for (final ISearchByViewPort connector : searchByViewPortConns) { + if (connector.isActive()) { + result.addSearchResult(connector.searchByViewport(viewport, tokens)); } } return result; @@ -185,7 +176,7 @@ public final class ConnectorFactory { return null; } - public static TrackableConnector[] getTrackableConnectors() { + public static Collection<TrackableConnector> getTrackableConnectors() { return TRACKABLE_CONNECTORS; } diff --git a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java index ac2fb37..3992013 100644 --- a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java +++ b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java @@ -4,6 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class GeocachingAustraliaConnector extends AbstractConnector { @@ -13,7 +14,7 @@ public class GeocachingAustraliaConnector extends AbstractConnector { } @Override - public String getCacheUrl(final Geocache cache) { + public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @@ -28,7 +29,7 @@ public class GeocachingAustraliaConnector extends AbstractConnector { } @Override - public boolean canHandle(final String geocode) { + public boolean canHandle(final @NonNull String geocode) { return (StringUtils.startsWithIgnoreCase(geocode, "GA") || StringUtils.startsWithIgnoreCase(geocode, "TP")) && isNumericId(geocode.substring(2)); } diff --git a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java index 500f752..aa08485 100644 --- a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java +++ b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java @@ -4,6 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class GeopeitusConnector extends AbstractConnector { @@ -13,7 +14,7 @@ public class GeopeitusConnector extends AbstractConnector { } @Override - public String getCacheUrl(final Geocache cache) { + public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + StringUtils.stripStart(cache.getGeocode().substring(2), "0"); } @@ -28,7 +29,7 @@ public class GeopeitusConnector extends AbstractConnector { } @Override - public boolean canHandle(String geocode) { + public boolean canHandle(@NonNull String geocode) { return StringUtils.startsWith(geocode, "GE") && isNumericId(geocode.substring(2)); } diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index a96ee10..34922f5 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -6,6 +6,9 @@ import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Collection; import java.util.List; public interface IConnector { @@ -17,20 +20,21 @@ public interface IConnector { public String getName(); /** - * return true, if this connector is responsible for the given cache + * Check if this connector is responsible for the given geocode. * * @param geocode - * @return + * geocode of a cache + * @return return {@code true}, if this connector is responsible for the cache */ - public boolean canHandle(final String geocode); + public boolean canHandle(final @NonNull String geocode); /** - * get browser URL for the given cache + * Get the browser URL for the given cache. * * @param cache * @return */ - public String getCacheUrl(final Geocache cache); + public String getCacheUrl(final @NonNull Geocache cache); /** * get long browser URL for the given cache @@ -38,7 +42,7 @@ public interface IConnector { * @param cache * @return */ - public String getLongCacheUrl(final Geocache cache); + public String getLongCacheUrl(final @NonNull Geocache cache); /** * enable/disable watchlist controls in cache details @@ -92,19 +96,19 @@ public interface IConnector { public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache); /** - * get host name of the connector server for dynamic loading of data + * Get host name of the connector server for dynamic loading of data. * * @return */ public String getHost(); /** - * get cache data license text + * Get cache data license text. This is displayed somewhere near the cache details. * * @param cache * @return */ - public String getLicenseText(final Geocache cache); + public String getLicenseText(final @NonNull Geocache cache); /** * enable/disable user actions in cache details @@ -178,13 +182,13 @@ public interface IConnector { public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt); /** - * Return true if this connector is activated for online - * interaction (download details, do searches, ...) + * Return {@code true} if this connector is active for online interaction (download details, do searches, ...). If + * this is {@code false}, the connector will still be used for already stored offline caches. * * @return */ - public boolean isActivated(); + public boolean isActive(); /** * Check if the current user is the owner of the given cache. @@ -213,7 +217,7 @@ public interface IConnector { public int getCacheMapMarkerId(boolean disabled); /** - * Get the list of <b>potentially</b> possible log types for a cache. Those may still be filter further during the + * Get the list of <b>potentially</b> possible log types for a cache. Those may still be filtered further during the * actual logging activity. * * @param geocache @@ -222,8 +226,8 @@ public interface IConnector { public List<LogType> getPossibleLogTypes(Geocache geocache); /** - * Get the gpx id for a waypoint when exporting. For some connectors there is an inherent name logic, - * for others its just the 'prefix' + * Get the GPX id for a waypoint when exporting. For some connectors there is an inherent name logic, + * for others its just the 'prefix'. * * @param prefix * @return @@ -231,10 +235,24 @@ public interface IConnector { public String getWaypointGpxId(String prefix, String geocode); /** - * Get the 'prefix' (key) for a waypoint from the 'name' in the gpx file - * + * Get the 'prefix' (key) for a waypoint from the 'name' in the GPX file + * * @param name * @return */ public String getWaypointPrefix(String name); + + /** + * Get the maximum value for Terrain + * + * @return + */ + public int getMaxTerrain(); + + /** + * Get a user readable collection of all online features of this connector. + * + * @return + */ + public Collection<String> getCapabilities(); } diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java index 04a73c1..e2e5d4c 100644 --- a/main/src/cgeo/geocaching/connector/NoLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java @@ -11,7 +11,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.List; -public class NoLoggingManager implements ILoggingManager { +public class NoLoggingManager extends AbstractLoggingManager { @Override public void init() { @@ -34,11 +34,6 @@ public class NoLoggingManager implements ILoggingManager { } @Override - public List<TrackableLog> getTrackables() { - return Collections.emptyList(); - } - - @Override public List<LogType> getPossibleLogTypes() { return Collections.emptyList(); } diff --git a/main/src/cgeo/geocaching/connector/UnknownConnector.java b/main/src/cgeo/geocaching/connector/UnknownConnector.java index e9fecb9..05593d7 100644 --- a/main/src/cgeo/geocaching/connector/UnknownConnector.java +++ b/main/src/cgeo/geocaching/connector/UnknownConnector.java @@ -4,6 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class UnknownConnector extends AbstractConnector { @@ -13,7 +14,7 @@ public class UnknownConnector extends AbstractConnector { } @Override - public String getCacheUrl(Geocache cache) { + public String getCacheUrl(@NonNull Geocache cache) { return null; // we have no url for these caches } @@ -28,7 +29,7 @@ public class UnknownConnector extends AbstractConnector { } @Override - public boolean canHandle(final String geocode) { + public boolean canHandle(final @NonNull String geocode) { return StringUtils.isNotBlank(geocode); } diff --git a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java index f184f6e..282ee31 100644 --- a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java +++ b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java @@ -4,6 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class WaymarkingConnector extends AbstractConnector { @@ -13,7 +14,7 @@ public class WaymarkingConnector extends AbstractConnector { } @Override - public String getCacheUrl(Geocache cache) { + public String getCacheUrl(@NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @@ -34,7 +35,7 @@ public class WaymarkingConnector extends AbstractConnector { } @Override - public boolean canHandle(String geocode) { + public boolean canHandle(@NonNull String geocode) { return StringUtils.startsWith(geocode, "WM"); } } diff --git a/main/src/cgeo/geocaching/connector/capability/ICredentials.java b/main/src/cgeo/geocaching/connector/capability/ICredentials.java new file mode 100644 index 0000000..2d5cd0b --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ICredentials.java @@ -0,0 +1,21 @@ +package cgeo.geocaching.connector.capability; + + +/** + * Marker interface for connectors which have user name and password as credentials. + * + */ +public interface ICredentials { + /** + * Get preference key of the user name. + * + */ + public int getUsernamePreferenceKey(); + + /** + * Get preference key of the password. + * + */ + public int getPasswordPreferenceKey(); + +} diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java index 91dd094..adc36c7 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java @@ -3,11 +3,14 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.loaders.RecaptchaReceiver; + +import org.eclipse.jdt.annotation.NonNull; /** * connector capability for online searching caches around a center coordinate, sorted by distance * */ public interface ISearchByCenter extends IConnector { - public SearchResult searchByCenter(final Geopoint center); + public SearchResult searchByCenter(final @NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java index 4c16049..7abc235 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java @@ -4,10 +4,12 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.utils.CancellableHandler; +import org.eclipse.jdt.annotation.Nullable; + /** * connector capability of searching online for a cache by geocode * */ public interface ISearchByGeocode extends IConnector { - public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler); + public SearchResult searchByGeocode(final @Nullable String geocode, final @Nullable String guid, final CancellableHandler handler); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java index 09b2423..9b3e6eb 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java @@ -2,11 +2,15 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.loaders.RecaptchaReceiver; + +import org.eclipse.jdt.annotation.NonNull; /** - * connector capability of searching online for a cache by name - * + * Connector capability of searching online for a cache by keyword. + * */ public interface ISearchByKeyword extends IConnector { - public SearchResult searchByName(final String name); + // TODO: The recaptcha receiver is only needed for GC. Would be good to refactor this away from the generic interface. + public SearchResult searchByKeyword(final @NonNull String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index 4954017..7981ba8 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -2,8 +2,11 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.geopoint.Viewport; +import org.eclipse.jdt.annotation.NonNull; + public interface ISearchByViewPort extends IConnector { - public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); + public SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens); } diff --git a/main/src/cgeo/geocaching/connector/ec/ECApi.java b/main/src/cgeo/geocaching/connector/ec/ECApi.java new file mode 100644 index 0000000..f91bcdb --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECApi.java @@ -0,0 +1,220 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.DataStore; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.connector.LogResult; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.files.GPX10Parser; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.list.StoredList; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.FastDateFormat; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +public class ECApi { + + private static final String API_HOST = "http://extremcaching.com/exports/"; + + private static final FastDateFormat LOG_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC"), Locale.US); + + public static String cleanCode(String geocode) { + return geocode.replace("EC", ""); + } + + public static Geocache searchByGeoCode(final String geocode) { + final Parameters params = new Parameters("id", cleanCode(geocode), "cgeo", "1"); + final HttpResponse response = apiRequest("gpx.php", params); + + final Collection<Geocache> caches = importCachesFromGPXResponse(response); + if (CollectionUtils.isNotEmpty(caches)) { + return caches.iterator().next(); + } + return null; + } + + public static Collection<Geocache> searchByBBox(final Viewport viewport) { + + if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { + return Collections.emptyList(); + } + + final Parameters params = new Parameters("fnc", "bbox"); + params.add("lat1", String.valueOf(viewport.getLatitudeMin())); + params.add("lat2", String.valueOf(viewport.getLatitudeMax())); + params.add("lon1", String.valueOf(viewport.getLongitudeMin())); + params.add("lon2", String.valueOf(viewport.getLongitudeMax())); + final HttpResponse response = apiRequest(params); + + return importCachesFromJSON(response); + } + + + public static Collection<Geocache> searchByCenter(final Geopoint center) { + + final Parameters params = new Parameters("fnc", "center"); + params.add("distance", "20"); + params.add("lat", String.valueOf(center.getLatitude())); + params.add("lon", String.valueOf(center.getLongitude())); + final HttpResponse response = apiRequest(params); + + return importCachesFromJSON(response); + } + + public static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log) { + return postLog(cache, logType, date, log, false); + } + + public static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, boolean isRetry) { + final Parameters params = new Parameters("cache_id", cache.getGeocode()); + params.add("type", logType.type); + params.add("log", log); + params.add("date", LOG_DATE_FORMAT.format(date.getTime())); + + final String uri = API_HOST + "log.php"; + final HttpResponse response = Network.postRequest(uri, params); + + if (response == null) { + return new LogResult(StatusCode.LOG_POST_ERROR_EC, ""); + } + if (!isRetry && response.getStatusLine().getStatusCode() == 403) { + if (ECLogin.getInstance().login() == StatusCode.NO_ERROR) { + apiRequest(uri, params, true); + } + } + if (response.getStatusLine().getStatusCode() != 200) { + return new LogResult(StatusCode.LOG_POST_ERROR_EC, ""); + } + + final String data = Network.getResponseDataAlways(response); + if (!StringUtils.isBlank(data) && StringUtils.contains(data, "success")) { + final String uid = StringUtils.remove(data, "success:"); + return new LogResult(StatusCode.NO_ERROR, uid); + } + + return new LogResult(StatusCode.LOG_POST_ERROR_EC, ""); + } + + + private static HttpResponse apiRequest(final Parameters params) { + return apiRequest("api.php", params, false); + } + + private static HttpResponse apiRequest(final String uri, final Parameters params) { + return apiRequest(uri, params, false); + } + + private static HttpResponse apiRequest(final String uri, final Parameters params, final boolean isRetry) { + final HttpResponse response = Network.getRequest(API_HOST + uri, params); + + if (response == null) { + return null; + } + if (!isRetry && response.getStatusLine().getStatusCode() == 403) { + if (ECLogin.getInstance().login() == StatusCode.NO_ERROR) { + apiRequest(uri, params, true); + } + } + if (response.getStatusLine().getStatusCode() != 200) { + return null; + } + return response; + } + + private static Collection<Geocache> importCachesFromGPXResponse(final HttpResponse response) { + if (response == null) { + return Collections.emptyList(); + } + + try { + return new GPX10Parser(StoredList.TEMPORARY_LIST_ID).parse(response.getEntity().getContent(), null); + } catch (Exception e) { + Log.e("Error importing gpx from extremcaching.com", e); + return Collections.emptyList(); + } + } + + private static List<Geocache> importCachesFromJSON(final HttpResponse response) { + + if (response != null) { + try { + final String data = Network.getResponseDataAlways(response); + if (StringUtils.isBlank(data) || StringUtils.equals(data, "[]")) { + return Collections.emptyList(); + } + final JSONArray json = new JSONArray(data); + final int len = json.length(); + final List<Geocache> caches = new ArrayList<Geocache>(len); + for (int i = 0; i < len; i++) { + final Geocache cache = parseCache(json.getJSONObject(i)); + if (cache != null) { + caches.add(cache); + } + } + return caches; + } catch (final JSONException e) { + Log.w("JSONResult", e); + } + } + + return Collections.emptyList(); + + } + + private static Geocache parseCache(final JSONObject response) { + final Geocache cache = new Geocache(); + cache.setReliableLatLon(true); + try { + cache.setGeocode("EC" + response.getString("cache_id")); + cache.setName(response.getString("title")); + cache.setCoords(new Geopoint(response.getString("lat"), response.getString("lon"))); + cache.setType(getCacheType(response.getString("type"))); + cache.setDifficulty((float) response.getDouble("difficulty")); + cache.setTerrain((float) response.getDouble("terrain")); + cache.setFound(response.getInt("found") == 1 ? true : false); + + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); + } catch (final JSONException e) { + Log.e("ECApi.parseCache", e); + return null; + } + return cache; + } + + private static CacheType getCacheType(final String cacheType) { + if (cacheType.equalsIgnoreCase("Tradi")) { + return CacheType.TRADITIONAL; + } + if (cacheType.equalsIgnoreCase("Multi")) { + return CacheType.MULTI; + } + if (cacheType.equalsIgnoreCase("Event")) { + return CacheType.EVENT; + } + if (cacheType.equalsIgnoreCase("Mystery")) { + return CacheType.MYSTERY; + } + return CacheType.UNKNOWN; + } +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECConnector.java b/main/src/cgeo/geocaching/connector/ec/ECConnector.java new file mode 100644 index 0000000..3c66d7d --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECConnector.java @@ -0,0 +1,232 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.ICache; +import cgeo.geocaching.LogCacheActivity; +import cgeo.geocaching.R; +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.capability.ICredentials; +import cgeo.geocaching.connector.capability.ILogin; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.connector.gc.MapTokens; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.loaders.RecaptchaReceiver; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.settings.SettingsActivity; +import cgeo.geocaching.utils.CancellableHandler; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import android.content.Context; +import android.os.Handler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +public class ECConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin, ICredentials { + + private static final String CACHE_URL = "http://extremcaching.com/index.php/output-2/"; + + /** + * Pattern for EC codes + */ + private final static Pattern PATTERN_EC_CODE = Pattern.compile("EC[0-9]+", Pattern.CASE_INSENSITIVE); + + private ECConnector() { + // singleton + } + + /** + * initialization on demand holder pattern + */ + private static class Holder { + private static final @NonNull ECConnector INSTANCE = new ECConnector(); + } + + public static @NonNull + ECConnector getInstance() { + return Holder.INSTANCE; + } + + @Override + public boolean canHandle(@NonNull String geocode) { + return ECConnector.PATTERN_EC_CODE.matcher(geocode).matches(); + } + + @Override + public String getCacheUrl(@NonNull Geocache cache) { + return CACHE_URL + cache.getGeocode().replace("EC", ""); + } + + @Override + public String getName() { + return "extremcaching.com"; + } + + @Override + public String getHost() { + return "extremcaching.com"; + } + + @Override + public SearchResult searchByGeocode(final @Nullable String geocode, final @Nullable String guid, final CancellableHandler handler) { + if (geocode == null) { + return null; + } + CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage); + + final Geocache cache = ECApi.searchByGeoCode(geocode); + + return cache != null ? new SearchResult(cache) : null; + } + + @Override + public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + final Collection<Geocache> caches = ECApi.searchByBBox(viewport); + if (caches == null) { + return null; + } + final SearchResult searchResult = new SearchResult(caches); + return searchResult.filterSearchResults(false, false, Settings.getCacheType()); + } + + @Override + public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + final Collection<Geocache> caches = ECApi.searchByCenter(center); + if (caches == null) { + return null; + } + final SearchResult searchResult = new SearchResult(caches); + return searchResult.filterSearchResults(false, false, Settings.getCacheType()); + } + + @Override + public boolean isOwner(final ICache cache) { + return false; + } + + @Override + protected String getCacheUrlPrefix() { + return CACHE_URL; + } + + @Override + public boolean isActive() { + return Settings.isECConnectorActive(); + } + + @Override + public boolean login(Handler handler, Context fromActivity) { + // login + final StatusCode status = ECLogin.getInstance().login(); + + if (status == StatusCode.NO_ERROR) { + CgeoApplication.getInstance().checkLogin = false; + } + + if (CgeoApplication.getInstance().showLoginToast && handler != null) { + handler.sendMessage(handler.obtainMessage(0, status)); + CgeoApplication.getInstance().showLoginToast = false; + + // invoke settings activity to insert login details + if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { + SettingsActivity.jumpToServicesPage(fromActivity); + } + } + return status == StatusCode.NO_ERROR; + } + + @Override + public String getUserName() { + return ECLogin.getInstance().getActualUserName(); + } + + @Override + public int getCachesFound() { + return ECLogin.getInstance().getActualCachesFound(); + } + + @Override + public String getLoginStatusString() { + return ECLogin.getInstance().getActualStatus(); + } + + @Override + public boolean isLoggedIn() { + return ECLogin.getInstance().isActualLoginStatus(); + } + + @Override + public int getCacheMapMarkerId(boolean disabled) { + final String icons = Settings.getECIconSet(); + if (StringUtils.equals(icons, "1")) { + return disabled ? R.drawable.marker_disabled_other : R.drawable.marker_other; + } + return disabled ? R.drawable.marker_disabled_oc : R.drawable.marker_oc; + } + + @Override + public String getLicenseText(final @NonNull Geocache cache) { + // NOT TO BE TRANSLATED + return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, CC BY-NC-ND 3.0, alle Logeinträge © jeweiliger Autor"; + } + + @Override + public boolean supportsLogging() { + return true; + } + + @Override + public boolean canLog(Geocache cache) { + return true; + } + + @Override + public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + return new ECLoggingManager(activity, this, cache); + } + + @Override + public List<LogType> getPossibleLogTypes(Geocache geocache) { + final List<LogType> logTypes = new ArrayList<LogType>(); + if (geocache.isEventCache()) { + logTypes.add(LogType.WILL_ATTEND); + logTypes.add(LogType.ATTENDED); + } else { + logTypes.add(LogType.FOUND_IT); + } + if (!geocache.isEventCache()) { + logTypes.add(LogType.DIDNT_FIND_IT); + } + logTypes.add(LogType.NOTE); + return logTypes; + } + + @Override + public int getMaxTerrain() { + return 7; + } + + @Override + public int getUsernamePreferenceKey() { + return R.string.pref_ecusername; + } + + @Override + public int getPasswordPreferenceKey() { + return R.string.pref_ecpassword; + } + +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECConstants.java b/main/src/cgeo/geocaching/connector/ec/ECConstants.java new file mode 100644 index 0000000..7c02d8e --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECConstants.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.connector.ec; + +import java.util.regex.Pattern; + +/** + * For further information about patterns have a look at + * http://download.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html + */ +public final class ECConstants { + + public static final Pattern PATTERN_LOGIN_NAME = Pattern.compile("\"mod_login_greetingfrontpage-teaser\">Hallo, ([^<]+)</span>"); + public static final Pattern PATTERN_LOGIN_SECURITY = Pattern.compile("<input type=\"hidden\" name=\"return\" value=\"(.*)\" />[^<]*<input type=\"hidden\" name=\"(.*)\" value=\"1\" />"); + public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("Gefundene Caches.*?>([0-9]+)<"); + + private ECConstants() { + // this class shall not have instances + } + +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java new file mode 100644 index 0000000..ded2d71 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java @@ -0,0 +1,48 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogCacheActivity; +import cgeo.geocaching.TrackableLog; +import cgeo.geocaching.connector.AbstractLoggingManager; +import cgeo.geocaching.connector.ImageResult; +import cgeo.geocaching.connector.LogResult; +import cgeo.geocaching.enumerations.LogType; + +import android.net.Uri; + +import java.util.Calendar; +import java.util.List; + +public class ECLoggingManager extends AbstractLoggingManager { + + private final ECConnector connector; + private final Geocache cache; + private LogCacheActivity activity; + + public ECLoggingManager(final LogCacheActivity activity, final ECConnector connector, final Geocache cache) { + this.connector = connector; + this.cache = cache; + this.activity = activity; + } + + @Override + public final void init() { + activity.onLoadFinished(); + } + + @Override + public final LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + return ECApi.postLog(cache, logType, date, log); + } + + @Override + public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { + return null; + } + + @Override + public List<LogType> getPossibleLogTypes() { + return connector.getPossibleLogTypes(cache); + } + +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECLogin.java b/main/src/cgeo/geocaching/connector/ec/ECLogin.java new file mode 100644 index 0000000..089688e --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECLogin.java @@ -0,0 +1,138 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; +import cgeo.geocaching.connector.AbstractLogin; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.TextUtils; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.regex.Matcher; + +public class ECLogin extends AbstractLogin { + + private ECLogin() { + // singleton + } + + private static class SingletonHolder { + private static ECLogin INSTANCE = new ECLogin(); + } + + public static ECLogin getInstance() { + return SingletonHolder.INSTANCE; + } + + @Override + protected StatusCode login(boolean retry) { + final ImmutablePair<String, String> login = Settings.getCredentials(ECConnector.getInstance()); + + if (StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { + clearLoginInfo(); + Log.e("ECLogin.login: No login information stored"); + return StatusCode.NO_LOGIN_INFO_STORED; + } + + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); + HttpResponse loginResponse = Network.getRequest("https://extremcaching.com/community/profil1"); + String loginData = Network.getResponseData(loginResponse); + + if (StringUtils.isBlank(loginData)) { + Log.e("ECLogin.login: Failed to retrieve login page (1st)"); + return StatusCode.CONNECTION_FAILED_EC; // no login page + } + + if (getLoginStatus(loginData)) { + Log.i("Already logged in Extremcaching.com as " + login.left); + return StatusCode.NO_ERROR; // logged in + } + + final Parameters params = new Parameters( + "username", login.left, + "password", login.right, + "remember", "yes"); + + Matcher m = ECConstants.PATTERN_LOGIN_SECURITY.matcher(loginData); + if (m.find() && m.groupCount() == 2) { + params.add("return", m.group(1)); + params.add(m.group(2), "1"); + } else { + Log.e("ECLogin.login security tokens in login form not found"); + return StatusCode.COMMUNICATION_ERROR; + } + + loginResponse = Network.postRequest("http://extremcaching.com/component/users/?task=user.login", params); + loginData = Network.getResponseData(loginResponse); + + if (StringUtils.isBlank(loginData)) { + Log.e("ECLogin.login: Failed to retrieve login page (2nd)"); + return StatusCode.COMMUNICATION_ERROR; // no login page + } + assert loginData != null; // Caught above + + if (getLoginStatus(loginData)) { + Log.i("Successfully logged in Extremcaching.com as " + login.left); + + Settings.setCookieStore(Cookies.dumpCookieStore()); + + return StatusCode.NO_ERROR; // logged in + } + + if (loginData.contains("Benutzername und Passwort falsch")) { // Yes, it's hardcoded in German (translation is done using Javascript and Google Translate) + Log.i("Failed to log in Extremcaching.com as " + login.left + " because of wrong username/password"); + return StatusCode.WRONG_LOGIN_DATA; // wrong login + } + + Log.i("Failed to log in Extremcaching.com as " + login.left + " for some unknown reason"); + if (retry) { + return login(false); + } + + return StatusCode.UNKNOWN_ERROR; // can't login + } + + + /** + * Check if the user has been logged in when he retrieved the data. + * + * @param page + * @return <code>true</code> if user is logged in, <code>false</code> otherwise + */ + public boolean getLoginStatus(@Nullable final String page) { + if (StringUtils.isBlank(page)) { + Log.e("ECLogin.getLoginStatus: No page given"); + return false; + } + assert page != null; + + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_ok)); + + // on every page except login page + setActualLoginStatus(TextUtils.matches(page, ECConstants.PATTERN_LOGIN_NAME)); + if (isActualLoginStatus()) { + setActualUserName(TextUtils.getMatch(page, ECConstants.PATTERN_LOGIN_NAME, true, "???")); + int cachesCount = 0; + try { + cachesCount = Integer.parseInt(TextUtils.getMatch(page, ECConstants.PATTERN_CACHES_FOUND, true, "0")); + } catch (final NumberFormatException e) { + Log.e("ECLogin.getLoginStatus: bad cache count", e); + } + setActualCachesFound(cachesCount); + return true; + } + + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_failed)); + return false; + } + +} diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 6a61405..4349d5d 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -9,13 +9,16 @@ import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.capability.ICredentials; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.utils.CancellableHandler; @@ -23,13 +26,15 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.content.Context; import android.os.Handler; import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ISearchByKeyword, ILogin, ICredentials { private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser @@ -53,28 +58,26 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * initialization on demand holder pattern */ private static class Holder { - private static final GCConnector INSTANCE = new GCConnector(); + private static final @NonNull GCConnector INSTANCE = new GCConnector(); } - public static GCConnector getInstance() { + public static @NonNull + GCConnector getInstance() { return Holder.INSTANCE; } @Override - public boolean canHandle(String geocode) { - if (geocode == null) { - return false; - } + public boolean canHandle(@NonNull String geocode) { return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches(); } @Override - public String getLongCacheUrl(Geocache cache) { + public String getLongCacheUrl(@NonNull Geocache cache) { return CACHE_URL_LONG + cache.getGeocode(); } @Override - public String getCacheUrl(Geocache cache) { + public String getCacheUrl(@NonNull Geocache cache) { return CACHE_URL_SHORT + cache.getGeocode(); } @@ -129,7 +132,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { + public SearchResult searchByGeocode(final @Nullable String geocode, final @Nullable String guid, final CancellableHandler handler) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage); @@ -166,7 +169,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByViewport(Viewport viewport, String[] tokens) { + public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { return GCMap.searchByViewport(viewport, tokens); } @@ -209,8 +212,9 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * * This must not be called from the UI thread. * - * @param cache the cache to add - * @return <code>true</code> if the cache was sucessfully added, <code>false</code> otherwise + * @param cache + * the cache to add + * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ public static boolean addToFavorites(Geocache cache) { @@ -226,8 +230,9 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * * This must not be called from the UI thread. * - * @param cache the cache to add - * @return <code>true</code> if the cache was sucessfully added, <code>false</code> otherwise + * @param cache + * the cache to add + * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ public static boolean removeFromFavorites(Geocache cache) { @@ -266,9 +271,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByCenter(Geopoint center) { - // TODO make search by coordinate use this method. currently it is just a marker that this connector supports search by center - return null; + public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + return GCParser.searchByCoords(center, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } @Override @@ -282,7 +286,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean isActivated() { + public boolean isActive() { return Settings.isGCConnectorActive(); } @@ -297,11 +301,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public boolean login(Handler handler, Context fromActivity) { // login - final StatusCode status = Login.login(); + final StatusCode status = GCLogin.getInstance().login(); if (status == StatusCode.NO_ERROR) { CgeoApplication.getInstance().checkLogin = false; - Login.detectGcCustomDate(); + GCLogin.detectGcCustomDate(); } if (CgeoApplication.getInstance().showLoginToast && handler != null) { @@ -318,22 +322,22 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public String getUserName() { - return Login.getActualUserName(); + return GCLogin.getInstance().getActualUserName(); } @Override public int getCachesFound() { - return Login.getActualCachesFound(); + return GCLogin.getInstance().getActualCachesFound(); } @Override public String getLoginStatusString() { - return Login.getActualStatus(); + return GCLogin.getInstance().getActualStatus(); } @Override public boolean isLoggedIn() { - return Login.isActualLoginStatus(); + return GCLogin.getInstance().isActualLoginStatus(); } @Override @@ -353,4 +357,19 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } return prefix; } + + @Override + public SearchResult searchByKeyword(@NonNull String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver) { + return GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); + } + + @Override + public int getUsernamePreferenceKey() { + return R.string.pref_username; + } + + @Override + public int getPasswordPreferenceKey() { + return R.string.pref_password; + } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 8bed2ea..06d6411 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -5,7 +5,7 @@ import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.AbstractLoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; @@ -28,7 +28,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.List; -public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCallbacks<String> { +public class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.LoaderCallbacks<String> { private final LogCacheActivity activity; private final Geocache cache; @@ -60,7 +60,7 @@ public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCa hasLoaderError = true; } else { - viewstates = Login.getViewstates(page); + viewstates = GCLogin.getViewstates(page); trackables = GCParser.parseTrackableLog(page); possibleLogTypes = GCParser.parseTypes(page); diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/GCLogin.java index beb49f1..e072c8f 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLogin.java @@ -2,6 +2,7 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import cgeo.geocaching.connector.AbstractLogin; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; @@ -13,6 +14,7 @@ import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -29,18 +31,12 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -public abstract class Login { +public class GCLogin extends AbstractLogin { private static final String DEFAULT_CUSTOM_DATE_FORMAT = "MM/dd/yyyy"; private final static String ENGLISH = "<a href=\"#\">English▼</a>"; - // false = not logged in - private static boolean actualLoginStatus = false; - private static String actualUserName = StringUtils.EMPTY; - private static int actualCachesFound = -1; - private static String actualStatus = StringUtils.EMPTY; - private final static Map<String, SimpleDateFormat> GC_CUSTOM_DATE_FORMATS; public static final String LANGUAGE_CHANGE_URI = "http://www.geocaching.com/my/souvenirs.aspx"; @@ -64,20 +60,31 @@ public abstract class Login { GC_CUSTOM_DATE_FORMATS = Collections.unmodifiableMap(map); } - public static StatusCode login() { - return login(true); + private GCLogin() { + // singleton } - private static StatusCode login(boolean retry) { - final ImmutablePair<String, String> login = Settings.getGcLogin(); + public static GCLogin getInstance() { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder { + private static final GCLogin INSTANCE = new GCLogin(); + } - if (StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { + @Override + protected StatusCode login(boolean retry) { + final ImmutablePair<String, String> credentials = Settings.getGcCredentials(); + final String username = credentials.left; + final String password = credentials.right; + + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { clearLoginInfo(); Log.e("Login.login: No login information stored"); return StatusCode.NO_LOGIN_INFO_STORED; } - Login.setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); HttpResponse loginResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx"); String loginData = Network.getResponseData(loginResponse); if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { @@ -86,12 +93,12 @@ public abstract class Login { if (StringUtils.isBlank(loginData)) { Log.e("Login.login: Failed to retrieve login page (1st)"); - return StatusCode.CONNECTION_FAILED; // no loginpage + return StatusCode.CONNECTION_FAILED; // no login page } - if (Login.getLoginStatus(loginData)) { - Log.i("Already logged in Geocaching.com as " + login.left + " (" + Settings.getMemberStatus() + ')'); - Login.switchToEnglish(loginData); + if (getLoginStatus(loginData)) { + Log.i("Already logged in Geocaching.com as " + username + " (" + Settings.getMemberStatus() + ')'); + switchToEnglish(loginData); return StatusCode.NO_ERROR; // logged in } @@ -101,16 +108,16 @@ public abstract class Login { final Parameters params = new Parameters( "__EVENTTARGET", "", "__EVENTARGUMENT", "", - "ctl00$ContentBody$tbUsername", login.left, - "ctl00$ContentBody$tbPassword", login.right, + "ctl00$ContentBody$tbUsername", username, + "ctl00$ContentBody$tbPassword", password, "ctl00$ContentBody$cbRememberMe", "on", "ctl00$ContentBody$btnSignIn", "Login"); - final String[] viewstates = Login.getViewstates(loginData); + final String[] viewstates = GCLogin.getViewstates(loginData); if (isEmpty(viewstates)) { Log.e("Login.login: Failed to find viewstates"); return StatusCode.LOGIN_PARSE_ERROR; // no viewstates } - Login.putViewstates(params, viewstates); + GCLogin.putViewstates(params, viewstates); loginResponse = Network.postRequest("https://www.geocaching.com/login/default.aspx", params); loginData = Network.getResponseData(loginResponse); @@ -122,35 +129,35 @@ public abstract class Login { } assert loginData != null; // Caught above - if (Login.getLoginStatus(loginData)) { - Log.i("Successfully logged in Geocaching.com as " + login.left + " (" + Settings.getMemberStatus() + ')'); + if (getLoginStatus(loginData)) { + Log.i("Successfully logged in Geocaching.com as " + username + " (" + Settings.getMemberStatus() + ')'); - Login.switchToEnglish(loginData); + switchToEnglish(loginData); Settings.setCookieStore(Cookies.dumpCookieStore()); return StatusCode.NO_ERROR; // logged in } if (loginData.contains("Your username/password combination does not match.")) { - Log.i("Failed to log in Geocaching.com as " + login.left + " because of wrong username/password"); + Log.i("Failed to log in Geocaching.com as " + username + " because of wrong username/password"); return StatusCode.WRONG_LOGIN_DATA; // wrong login } if (loginData.contains("You must validate your account before you can log in.")) { - Log.i("Failed to log in Geocaching.com as " + login.left + " because account needs to be validated first"); + Log.i("Failed to log in Geocaching.com as " + username + " because account needs to be validated first"); return StatusCode.UNVALIDATED_ACCOUNT; } - Log.i("Failed to log in Geocaching.com as " + login.left + " for some unknown reason"); + Log.i("Failed to log in Geocaching.com as " + username + " for some unknown reason"); if (retry) { - Login.switchToEnglish(loginData); + switchToEnglish(loginData); return login(false); } return StatusCode.UNKNOWN_ERROR; // can't login } - public static StatusCode logout() { + public StatusCode logout() { final HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f"); final String logoutData = Network.getResponseData(logoutResponse); if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { @@ -162,51 +169,6 @@ public abstract class Login { return StatusCode.NO_ERROR; } - private static void resetLoginStatus() { - Cookies.clearCookies(); - Settings.setCookieStore(null); - - setActualLoginStatus(false); - } - - private static void clearLoginInfo() { - resetLoginStatus(); - - setActualCachesFound(-1); - setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); - } - - static void setActualCachesFound(final int found) { - actualCachesFound = found; - } - - public static String getActualStatus() { - return actualStatus; - } - - private static void setActualStatus(final String status) { - actualStatus = status; - } - - public static boolean isActualLoginStatus() { - return actualLoginStatus; - } - - private static void setActualLoginStatus(boolean loginStatus) { - actualLoginStatus = loginStatus; - } - - public static String getActualUserName() { - return actualUserName; - } - - private static void setActualUserName(String userName) { - actualUserName = userName; - } - - public static int getActualCachesFound() { - return actualCachesFound; - } /** * Check if the user has been logged in when he retrieved the data. @@ -214,7 +176,7 @@ public abstract class Login { * @param page * @return <code>true</code> if user is logged in, <code>false</code> otherwise */ - public static boolean getLoginStatus(@Nullable final String page) { + public boolean getLoginStatus(@Nullable final String page) { if (StringUtils.isBlank(page)) { Log.e("Login.checkLogin: No page given"); return false; @@ -253,7 +215,7 @@ public abstract class Login { return false; } - private static void switchToEnglish(String previousPage) { + private void switchToEnglish(String previousPage) { if (previousPage != null && previousPage.contains(ENGLISH)) { Log.i("Geocaching.com language already set to English"); // get find count @@ -267,7 +229,7 @@ public abstract class Login { final Parameters params = new Parameters( "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english "__EVENTARGUMENT", ""); - Login.transferViewstates(page, params); + GCLogin.transferViewstates(page, params); final HttpResponse response = Network.postRequest(LANGUAGE_CHANGE_URI, params, new Parameters("Referer", LANGUAGE_CHANGE_URI)); if (Network.isSuccess(response)) { Log.i("changed language on geocaching.com to English"); @@ -277,7 +239,7 @@ public abstract class Login { } } - public static BitmapDrawable downloadAvatarAndGetMemberStatus() { + public BitmapDrawable downloadAvatarAndGetMemberStatus() { try { final String responseData = StringUtils.defaultString(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); final String profile = TextUtils.replaceWhitespace(responseData); @@ -455,7 +417,7 @@ public abstract class Login { * @param uri * @return */ - public static String postRequestLogged(final String uri, final Parameters params) { + public String postRequestLogged(final String uri, final Parameters params) { final String data = Network.getResponseData(Network.postRequest(uri, params)); if (getLoginStatus(data)) { @@ -478,7 +440,7 @@ public abstract class Login { * @return */ @Nullable - public static String getRequestLogged(@NonNull final String uri, @Nullable final Parameters params) { + public String getRequestLogged(@NonNull final String uri, @Nullable final Parameters params) { final HttpResponse response = Network.getRequest(uri, params); final String data = Network.getResponseData(response, canRemoveWhitespace(uri)); @@ -506,12 +468,18 @@ public abstract class Login { return !StringUtils.contains(uri, "cache_details"); } - /** Get user session & session token from the Live Map. Needed for following requests */ - public static String[] getMapTokens() { + /** + * Get user session & session token from the Live Map. Needed for following requests. + * + * @return first is user session, second is session token + */ + public static @NonNull + MapTokens getMapTokens() { final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP); final String data = Network.getResponseData(response); final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); - return new String[] { userSession, sessionToken }; + return new MapTokens(userSession, sessionToken); } + } diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index e2c7dfa..aabeb56 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,9 +1,9 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; @@ -85,7 +85,7 @@ public class GCMap { cache.setDifficulty(Float.parseFloat(difficultyObj.getString("text"))); // 3.5 JSONObject terrainObj = dataObject.getJSONObject("terrain"); cache.setTerrain(Float.parseFloat(terrainObj.getString("text"))); // 1.5 - cache.setHidden(Login.parseGcCustomDate(dataObject.getString("hidden"), "MM/dd/yyyy")); // 7/23/2001 + cache.setHidden(GCLogin.parseGcCustomDate(dataObject.getString("hidden"), "MM/dd/yyyy")); // 7/23/2001 JSONObject containerObj = dataObject.getJSONObject("container"); cache.setSize(CacheSize.getById(containerObj.getString("text"))); // Regular JSONObject typeObj = dataObject.getJSONObject("type"); @@ -254,7 +254,7 @@ public class GCMap { * Live map tokens * @return */ - public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { + public static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens) { int speed = (int) CgeoApplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h Strategy strategy = Settings.getLiveMapStrategy(); if (strategy == Strategy.AUTO) { @@ -284,7 +284,7 @@ public class GCMap { * Strategy for data retrieval and parsing, @see Strategy * @return */ - private static SearchResult searchByViewport(final Viewport viewport, final String[] tokens, Strategy strategy) { + private static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens, Strategy strategy) { Log.d("GCMap.searchByViewport" + viewport.toString()); final SearchResult searchResult = new SearchResult(); @@ -310,7 +310,7 @@ public class GCMap { "ep", "1", "app", "cgeo"); if (tokens != null) { - params.put("k", tokens[0], "st", tokens[1]); + params.put("k", tokens.getUserSession(), "st", tokens.getSessionToken()); } if (Settings.isExcludeMyCaches()) { // works only for PM params.put("hf", "1", "hh", "1"); // hide found, hide hidden diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 4f5d293..f05bc04 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -79,7 +79,7 @@ public abstract class GCParser { final SearchResult searchResult = new SearchResult(); searchResult.setUrl(url); - searchResult.viewstates = Login.getViewstates(page); + searchResult.viewstates = GCLogin.getViewstates(page); // recaptcha if (showCaptcha) { @@ -203,7 +203,7 @@ public abstract class GCParser { final String dateHidden = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_HIDDEN_DATE, false, 1, null, false); if (StringUtils.isNotBlank(dateHidden)) { try { - Date date = Login.parseGcCustomDate(dateHidden); + Date date = GCLogin.parseGcCustomDate(dateHidden); if (date != null) { cache.setHidden(date); } @@ -268,7 +268,7 @@ public abstract class GCParser { try { final String result = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); if (null != result) { - searchResult.setTotal(Integer.parseInt(result) - excludedCaches); + searchResult.setTotalCountGC(Integer.parseInt(result) - excludedCaches); } } catch (final NumberFormatException e) { Log.w("GCParser.parseSearch: Failed to parse cache count"); @@ -276,10 +276,7 @@ public abstract class GCParser { String recaptchaText = null; if (thread != null && StringUtils.isNotBlank(thread.getChallenge())) { - if (thread.getText() == null) { - thread.waitForUser(); - } - + thread.waitForUser(); recaptchaText = thread.getText(); } @@ -465,13 +462,13 @@ public abstract class GCParser { try { String hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); if (StringUtils.isNotBlank(hiddenString)) { - cache.setHidden(Login.parseGcCustomDate(hiddenString)); + cache.setHidden(GCLogin.parseGcCustomDate(hiddenString)); } if (cache.getHiddenDate() == null) { // event date hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); if (StringUtils.isNotBlank(hiddenString)) { - cache.setHidden(Login.parseGcCustomDate(hiddenString)); + cache.setHidden(GCLogin.parseGcCustomDate(hiddenString)); } } } catch (final ParseException e) { @@ -497,7 +494,7 @@ public abstract class GCParser { try { final String foundDateString = TextUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); if (StringUtils.isNotBlank(foundDateString)) { - cache.setVisitedDate(Login.parseGcCustomDate(foundDateString).getTime()); + cache.setVisitedDate(GCLogin.parseGcCustomDate(foundDateString).getTime()); } } catch (final ParseException e) { // failed to parse cache found date @@ -775,7 +772,7 @@ public abstract class GCParser { return search; } - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { Log.e("GCParser.searchByNextPage: No viewstate given"); return search; } @@ -786,10 +783,10 @@ public abstract class GCParser { final Parameters params = new Parameters( "__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08", "__EVENTARGUMENT", ""); - Login.putViewstates(params, viewstates); + GCLogin.putViewstates(params, viewstates); - final String page = Login.postRequestLogged(uri, params); - if (!Login.getLoginStatus(page)) { + final String page = GCLogin.getInstance().postRequestLogged(uri, params); + if (!GCLogin.getInstance().getLoginStatus(page)) { Log.e("GCParser.postLogTrackable: Can not log in geocaching"); return search; } @@ -853,7 +850,7 @@ public abstract class GCParser { final String uri = "http://www.geocaching.com/seek/nearest.aspx"; final String fullUri = uri + "?" + addFToParams(params, my, true); - final String page = Login.getRequestLogged(uri, addFToParams(params, my, true)); + final String page = GCLogin.getInstance().getRequestLogged(uri, addFToParams(params, my, true)); if (StringUtils.isBlank(page)) { Log.e("GCParser.searchByAny: No data from server"); @@ -869,17 +866,17 @@ public abstract class GCParser { final SearchResult search = searchResult.filterSearchResults(Settings.isExcludeDisabledCaches(), false, cacheType); - Login.getLoginStatus(page); + GCLogin.getInstance().getLoginStatus(page); return search; } - public static SearchResult searchByCoords(final Geopoint coords, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByCoords(final @NonNull Geopoint coords, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude())); return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByKeyword(final String keyword, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByKeyword(final @NonNull String keyword, final CacheType cacheType, final boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(keyword)) { Log.e("GCParser.searchByKeyword: No keyword given"); return null; @@ -890,7 +887,7 @@ public abstract class GCParser { } private static boolean isSearchForMyCaches(final String userName) { - if (userName.equalsIgnoreCase(Settings.getGcLogin().left)) { + if (userName.equalsIgnoreCase(Settings.getGcCredentials().left)) { Log.i("Overriding users choice because of self search, downloading all caches."); return true; } @@ -976,7 +973,7 @@ public abstract class GCParser { params.put("id", id); } - final String page = Login.getRequestLogged("http://www.geocaching.com/track/details.aspx", params); + final String page = GCLogin.getInstance().getRequestLogged("http://www.geocaching.com/track/details.aspx", params); if (StringUtils.isBlank(page)) { Log.e("GCParser.searchTrackable: No data from server"); @@ -997,7 +994,7 @@ public abstract class GCParser { final Parameters params = new Parameters(); - final String page = Login.getRequestLogged("http://www.geocaching.com/pocket/default.aspx", params); + final String page = GCLogin.getInstance().getRequestLogged("http://www.geocaching.com/pocket/default.aspx", params); if (StringUtils.isBlank(page)) { Log.e("GCParser.searchPocketQueryList: No data from server"); @@ -1043,7 +1040,7 @@ public abstract class GCParser { public static ImmutablePair<StatusCode, String> postLog(final String geocode, final String cacheid, final String[] viewstates, final LogType logType, final int year, final int month, final int day, final String log, final List<TrackableLog> trackables) { - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { Log.e("GCParser.postLog: No viewstate given"); return new ImmutablePair<StatusCode, String>(StatusCode.LOG_POST_ERROR, ""); } @@ -1064,7 +1061,7 @@ public abstract class GCParser { "__EVENTARGUMENT", "", "__LASTFOCUS", "", "ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id), - "ctl00$ContentBody$LogBookPanel1$uxDateVisited", Login.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime()), + "ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime()), "ctl00$ContentBody$LogBookPanel1$uxDateVisited$Month", Integer.toString(month), "ctl00$ContentBody$LogBookPanel1$uxDateVisited$Day", Integer.toString(day), "ctl00$ContentBody$LogBookPanel1$uxDateVisited$Year", Integer.toString(year), @@ -1077,7 +1074,7 @@ public abstract class GCParser { "ctl00$ContentBody$LogBookPanel1$btnSubmitLog", "Submit Log Entry", "ctl00$ContentBody$LogBookPanel1$uxLogCreationSource", "Old", "ctl00$ContentBody$uxVistOtherListingGC", ""); - Login.putViewstates(params, viewstates); + GCLogin.putViewstates(params, viewstates); if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed final StringBuilder hdnSelected = new StringBuilder(); @@ -1094,8 +1091,8 @@ public abstract class GCParser { } final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString(); - String page = Login.postRequestLogged(uri, params); - if (!Login.getLoginStatus(page)) { + String page = GCLogin.getInstance().postRequestLogged(uri, params); + if (!GCLogin.getInstance().getLoginStatus(page)) { Log.e("GCParser.postLog: Cannot log in geocaching"); return new ImmutablePair<StatusCode, String>(StatusCode.NOT_LOGGED_IN, ""); } @@ -1106,15 +1103,15 @@ public abstract class GCParser { try { if (matcher.find() && matcher.groupCount() > 0) { - final String[] viewstatesConfirm = Login.getViewstates(page); + final String[] viewstatesConfirm = GCLogin.getViewstates(page); - if (Login.isEmpty(viewstatesConfirm)) { + if (GCLogin.isEmpty(viewstatesConfirm)) { Log.e("GCParser.postLog: No viewstate for confirm log"); return new ImmutablePair<StatusCode, String>(StatusCode.LOG_POST_ERROR, ""); } params.clear(); - Login.putViewstates(params, viewstatesConfirm); + GCLogin.putViewstates(params, viewstatesConfirm); params.put("__EVENTTARGET", ""); params.put("__EVENTARGUMENT", ""); params.put("__LASTFOCUS", ""); @@ -1159,10 +1156,10 @@ public abstract class GCParser { DataStore.saveVisitDate(geocode); } - Login.getLoginStatus(page); + GCLogin.getInstance().getLoginStatus(page); // the log-successful-page contains still the old value - if (Login.getActualCachesFound() >= 0) { - Login.setActualCachesFound(Login.getActualCachesFound() + 1); + if (GCLogin.getInstance().getActualCachesFound() >= 0) { + GCLogin.getInstance().setActualCachesFound(GCLogin.getInstance().getActualCachesFound() + 1); } final String logID = TextUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, ""); @@ -1193,14 +1190,14 @@ public abstract class GCParser { public static ImmutablePair<StatusCode, String> uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) { final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString(); - final String page = Login.getRequestLogged(uri, null); + final String page = GCLogin.getInstance().getRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.uploadLogImage: No data from server"); return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); } assert page != null; - final String[] viewstates = Login.getViewstates(page); + final String[] viewstates = GCLogin.getViewstates(page); final Parameters uploadParams = new Parameters( "__EVENTTARGET", "", @@ -1208,7 +1205,7 @@ public abstract class GCParser { "ctl00$ContentBody$ImageUploadControl1$uxFileCaption", caption, "ctl00$ContentBody$ImageUploadControl1$uxFileDesc", description, "ctl00$ContentBody$ImageUploadControl1$uxUpload", "Upload"); - Login.putViewstates(uploadParams, viewstates); + GCLogin.putViewstates(uploadParams, viewstates); final File image = new File(imageUri.getPath()); final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image)); @@ -1232,7 +1229,7 @@ public abstract class GCParser { */ public static StatusCode postLogTrackable(final String tbid, final String trackingCode, final String[] viewstates, final LogType logType, final int year, final int month, final int day, final String log) { - if (Login.isEmpty(viewstates)) { + if (GCLogin.isEmpty(viewstates)) { Log.e("GCParser.postLogTrackable: No viewstate given"); return StatusCode.LOG_POST_ERROR; } @@ -1253,13 +1250,13 @@ public abstract class GCParser { "__LASTFOCUS", "", "ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id), "ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode); - Login.putViewstates(params, viewstates); + GCLogin.putViewstates(params, viewstates); if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) { params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", ""); params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", ""); } else { params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year)); - params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", Login.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime())); + params.put("ctl00$ContentBody$LogBookPanel1$uxDateVisited", GCLogin.getCustomGcDateFormat().format(new GregorianCalendar(year, month - 1, day).getTime())); } params.put( "ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day), @@ -1275,8 +1272,8 @@ public abstract class GCParser { "ctl00$ContentBody$uxVistOtherListingGC", ""); final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString(); - final String page = Login.postRequestLogged(uri, params); - if (!Login.getLoginStatus(page)) { + final String page = GCLogin.getInstance().postRequestLogged(uri, params); + if (!GCLogin.getInstance().getLoginStatus(page)) { Log.e("GCParser.postLogTrackable: Cannot log in geocaching"); return StatusCode.NOT_LOGGED_IN; } @@ -1305,7 +1302,7 @@ public abstract class GCParser { */ static boolean addToWatchlist(final Geocache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId(); - final String page = Login.postRequestLogged(uri, null); + final String page = GCLogin.getInstance().postRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.addToWatchlist: No data from server"); @@ -1331,7 +1328,7 @@ public abstract class GCParser { */ static boolean removeFromWatchlist(final Geocache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?ds=1&action=rem&id=" + cache.getCacheId(); - String page = Login.postRequestLogged(uri, null); + String page = GCLogin.getInstance().postRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.removeFromWatchlist: No data from server"); @@ -1343,7 +1340,7 @@ public abstract class GCParser { "__EVENTTARGET", "", "__EVENTARGUMENT", "", "ctl00$ContentBody$btnYes", "Yes"); - Login.transferViewstates(page, params); + GCLogin.transferViewstates(page, params); page = Network.getResponseData(Network.postRequest(uri, params)); final boolean guidOnPage = cache.isGuidContainedInPage(page); @@ -1367,7 +1364,7 @@ public abstract class GCParser { params.put("log", log); params.put("numlogs", numlogs); - return Login.getRequestLogged("http://www.geocaching.com/seek/cache_details.aspx", params); + return GCLogin.getInstance().getRequestLogged("http://www.geocaching.com/seek/cache_details.aspx", params); } /** @@ -1563,7 +1560,7 @@ public abstract class GCParser { while (matcherLogs.find()) { long date = 0; try { - date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime(); + date = GCLogin.parseGcCustomDate(matcherLogs.group(2)).getTime(); } catch (final ParseException e) { } @@ -1694,7 +1691,7 @@ public abstract class GCParser { long date = 0; try { - date = Login.parseGcCustomDate(entry.getString("Visited")).getTime(); + date = GCLogin.parseGcCustomDate(entry.getString("Visited")).getTime(); } catch (final ParseException e) { Log.e("GCParser.loadLogsFromDetails: failed to parse log date."); } diff --git a/main/src/cgeo/geocaching/connector/gc/MapTokens.java b/main/src/cgeo/geocaching/connector/gc/MapTokens.java new file mode 100644 index 0000000..78ce4cb --- /dev/null +++ b/main/src/cgeo/geocaching/connector/gc/MapTokens.java @@ -0,0 +1,23 @@ +package cgeo.geocaching.connector.gc; + +import android.util.Pair; + +/** + * Wrapper type to make map tokens more type safe than with a String array. + * + */ +public final class MapTokens extends Pair<String, String> { + + public MapTokens(String userSession, String sessionToken) { + super(userSession, sessionToken); + } + + public String getUserSession() { + return first; + } + + public String getSessionToken() { + return second; + } + +} diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java index d43d06b..46e4c96 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java @@ -8,6 +8,8 @@ import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.CryptUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; public class OCApiConnector extends OCConnector implements ISearchByGeocode { @@ -41,13 +43,13 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode { } @Override - public String getLicenseText(final Geocache cache) { + public String getLicenseText(final @NonNull Geocache cache) { // NOT TO BE TRANSLATED return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, " + licenseString; } @Override - public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { + public SearchResult searchByGeocode(final @Nullable String geocode, final @Nullable String guid, final CancellableHandler handler) { final Geocache cache = OkapiClient.getCache(geocode); if (cache == null) { return null; @@ -56,7 +58,7 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode { } @Override - public boolean isActivated() { + public boolean isActive() { // currently always active, but only for details download return true; } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index b1b9088..b45f809 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -10,13 +10,16 @@ import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CryptUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.content.Context; import android.os.Handler; @@ -39,17 +42,17 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean isActivated() { + public boolean isActive() { return Settings.isOCConnectorActive(isActivePrefKeyId); } @Override - public SearchResult searchByViewport(Viewport viewport, String[] tokens) { + public SearchResult searchByViewport(@NonNull Viewport viewport, MapTokens tokens) { return new SearchResult(OkapiClient.getCachesBBox(viewport, this)); } @Override - public SearchResult searchByCenter(Geopoint center) { + public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { return new SearchResult(OkapiClient.getCachesAround(center, this)); } @@ -155,7 +158,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public SearchResult searchByName(final String name) { + public SearchResult searchByKeyword(final @NonNull String name, final @NonNull RecaptchaReceiver recaptchaReceiver) { final Geopoint currentPos = CgeoApplication.getInstance().currentGeo().getCoords(); return new SearchResult(OkapiClient.getCachesNamed(currentPos, name, this)); } diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index b5c62ea..1ba88d5 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -6,6 +6,8 @@ import cgeo.geocaching.R; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.enumerations.LogType; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; @@ -27,10 +29,7 @@ public class OCConnector extends AbstractConnector { } @Override - public boolean canHandle(String geocode) { - if (geocode == null) { - return false; - } + public boolean canHandle(@NonNull String geocode) { return codePattern.matcher(geocode).matches(); } @@ -40,7 +39,7 @@ public class OCConnector extends AbstractConnector { } @Override - public String getCacheUrl(Geocache cache) { + public String getCacheUrl(@NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index c6be3cb..6836e6f 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -3,7 +3,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.TrackableLog; -import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.AbstractLoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; @@ -12,10 +12,9 @@ import cgeo.geocaching.enumerations.StatusCode; import android.net.Uri; import java.util.Calendar; -import java.util.Collections; import java.util.List; -public class OkapiLoggingManager implements ILoggingManager { +public class OkapiLoggingManager extends AbstractLoggingManager { private final OCApiLiveConnector connector; private final Geocache cache; @@ -45,16 +44,6 @@ public class OkapiLoggingManager implements ILoggingManager { } @Override - public final boolean hasLoaderError() { - return false; - } - - @Override - public final List<TrackableLog> getTrackables() { - return Collections.emptyList(); - } - - @Override public List<LogType> getPossibleLogTypes() { return connector.getPossibleLogTypes(cache); } diff --git a/main/src/cgeo/geocaching/connector/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java index af33bb6..7d4cf7f 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java +++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java @@ -3,13 +3,21 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByKeyword; +import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.loaders.RecaptchaReceiver; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CancellableHandler; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import java.util.Collection; import java.util.regex.Pattern; @@ -17,17 +25,17 @@ import java.util.regex.Pattern; * connector for OpenCaching.com * */ -public class OXConnector extends AbstractConnector implements ISearchByCenter, ISearchByGeocode { +public class OXConnector extends AbstractConnector implements ISearchByCenter, ISearchByGeocode, ISearchByViewPort, ISearchByKeyword { private static final Pattern PATTERN_GEOCODE = Pattern.compile("OX[A-Z0-9]+", Pattern.CASE_INSENSITIVE); @Override - public boolean canHandle(String geocode) { + public boolean canHandle(@NonNull String geocode) { return PATTERN_GEOCODE.matcher(geocode).matches(); } @Override - public String getCacheUrl(Geocache cache) { + public String getCacheUrl(@NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @@ -42,9 +50,9 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I } @Override - public String getLicenseText(Geocache cache) { + public String getLicenseText(@NonNull Geocache cache) { // NOT TO BE TRANSLATED - return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons BY-SA 3.0 License"; + return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons CC-BY-SA 3.0 License"; } @Override @@ -53,7 +61,10 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I } @Override - public SearchResult searchByGeocode(String geocode, String guid, CancellableHandler handler) { + public SearchResult searchByGeocode(final @Nullable String geocode, final @Nullable String guid, final CancellableHandler handler) { + if (geocode == null) { + return null; + } final Geocache cache = OpenCachingApi.searchByGeoCode(geocode); if (cache == null) { return null; @@ -63,16 +74,34 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I } @Override - public SearchResult searchByCenter(Geopoint center) { - Collection<Geocache> caches = OpenCachingApi.searchByCenter(center); - if (caches == null) { - return null; - } - return new SearchResult(caches); + public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + return createSearchResult(OpenCachingApi.searchByCenter(center)); } @Override protected String getCacheUrlPrefix() { return "http://www.opencaching.com/#!geocache/"; } + + @Override + public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + return createSearchResult(OpenCachingApi.searchByBoundingBox(viewport)); + } + + @Override + public boolean isActive() { + return Settings.isOXConnectorActive(); + } + + @Override + public SearchResult searchByKeyword(final @NonNull String name, final @NonNull RecaptchaReceiver recaptchaReceiver) { + return createSearchResult(OpenCachingApi.searchByKeyword(name)); + } + + private static SearchResult createSearchResult(Collection<Geocache> caches) { + if (caches == null) { + return null; + } + return new SearchResult(caches); + } } diff --git a/main/src/cgeo/geocaching/connector/ox/OXGPXParser.java b/main/src/cgeo/geocaching/connector/ox/OXGPXParser.java index f40a3e8..f72b698 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXGPXParser.java +++ b/main/src/cgeo/geocaching/connector/ox/OXGPXParser.java @@ -3,6 +3,9 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; import cgeo.geocaching.files.GPX10Parser; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + public class OXGPXParser extends GPX10Parser { private final boolean isDetailed; @@ -19,5 +22,16 @@ public class OXGPXParser extends GPX10Parser { cache.setDetailedUpdate(cache.getUpdated()); cache.setDetailed(true); } + removeTitleFromShortDescription(cache); + } + + /** + * The short description of OX caches contains "title by owner, type(T/D/Awesomeness)". That is a lot of + * duplication. + * + * @param cache + */ + private static void removeTitleFromShortDescription(final @NonNull Geocache cache) { + cache.setShortDescription(StringUtils.substringAfterLast(cache.getShortDescription(), ",")); } } diff --git a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java index 9d1dfc7..2defc52 100644 --- a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java +++ b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java @@ -3,6 +3,7 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; @@ -12,19 +13,20 @@ import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; import org.apache.commons.collections4.CollectionUtils; +import org.eclipse.jdt.annotation.NonNull; import java.util.Collection; import java.util.Collections; public class OpenCachingApi { + private static final String API_URL_CACHES_GPX = "http://www.opencaching.com/api/geocache.gpx"; private static final String DEV_KEY = CryptUtils.rot13("PtqQnHo9RUTht3Np"); - public static Geocache searchByGeoCode(final String geocode) { - final HttpResponse response = Network.getRequest("http://www.opencaching.com/api/geocache/" + geocode + ".gpx", + public static Geocache searchByGeoCode(final @NonNull String geocode) { + final HttpResponse response = getRequest("http://www.opencaching.com/api/geocache/" + geocode + ".gpx", new Parameters( - "Authorization", DEV_KEY, - "log_limit", "30", + "log_limit", "50", "hint", "true", "description", "html")); final Collection<Geocache> caches = importCachesFromResponse(response, true); @@ -34,13 +36,18 @@ public class OpenCachingApi { return null; } + private static HttpResponse getRequest(String string, Parameters parameters) { + parameters.add("Authorization", DEV_KEY); + return Network.getRequest(string, parameters); + } + private static Collection<Geocache> importCachesFromResponse(final HttpResponse response, final boolean isDetailed) { if (response == null) { return Collections.emptyList(); } Collection<Geocache> caches; try { - caches = new OXGPXParser(StoredList.STANDARD_LIST_ID, isDetailed).parse(response.getEntity().getContent(), null); + caches = new OXGPXParser(StoredList.TEMPORARY_LIST_ID, isDetailed).parse(response.getEntity().getContent(), null); } catch (Exception e) { Log.e("Error importing from OpenCaching.com", e); return Collections.emptyList(); @@ -48,17 +55,39 @@ public class OpenCachingApi { return caches; } - public static Collection<Geocache> searchByCenter(final Geopoint center) { - final HttpResponse response = Network.getRequest("http://www.opencaching.com/api/geocache/.gpx", + public static Collection<Geocache> searchByCenter(final @NonNull Geopoint center) { + final HttpResponse response = getRequest(API_URL_CACHES_GPX, new Parameters( - "Authorization", DEV_KEY, "log_limit", "0", "hint", "false", "description", "none", - "limit", "10", + "limit", "20", "center", center.format(GeopointFormatter.Format.LAT_LON_DECDEGREE_COMMA))); return importCachesFromResponse(response, false); } + public static Collection<Geocache> searchByBoundingBox(final @NonNull Viewport viewport) { + final String bbox = viewport.bottomLeft.format(GeopointFormatter.Format.LAT_LON_DECDEGREE_COMMA) + "," + viewport.topRight.format(GeopointFormatter.Format.LAT_LON_DECDEGREE_COMMA); + final HttpResponse response = getRequest(API_URL_CACHES_GPX, + new Parameters( + "log_limit", "0", + "hint", "false", + "description", "none", + "limit", "100", + "bbox", bbox)); + return importCachesFromResponse(response, false); + } + + public static Collection<Geocache> searchByKeyword(final @NonNull String name) { + final HttpResponse response = getRequest(API_URL_CACHES_GPX, + new Parameters( + "log_limit", "5", + "hint", "false", + "description", "none", + "limit", "100", + "name", name)); + return importCachesFromResponse(response, false); + } + } diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java index 67180b3..9cb5dc5 100644 --- a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java @@ -37,15 +37,15 @@ public class GeokretyParser { try { final int indexId = attributes.getIndex("id"); if (indexId > -1) { - trackable.setGeocode(geocode(Integer.valueOf(attributes.getValue("id")))); + trackable.setGeocode(geocode(Integer.parseInt(attributes.getValue("id")))); } final int indexDist = attributes.getIndex("dist"); if (indexDist > -1) { - trackable.setDistance(Float.valueOf(attributes.getValue("dist"))); + trackable.setDistance(Float.parseFloat(attributes.getValue("dist"))); } final int indexType = attributes.getIndex("type"); if (indexType > -1) { - trackable.setType(getType(Integer.valueOf(attributes.getValue("type")))); + trackable.setType(getType(Integer.parseInt(attributes.getValue("type")))); } final int indexWaypoint = attributes.getIndex("waypoint"); if (indexWaypoint > -1) { diff --git a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java index 31fc023..4ace4a8 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java +++ b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java @@ -79,7 +79,7 @@ public enum CacheAttribute { STROLLER(41, -1, "stroller", R.drawable.attribute_stroller, R.string.attribute_stroller_yes, R.string.attribute_stroller_no), FUEL(58, -1, "fuel", R.drawable.attribute_fuel, R.string.attribute_fuel_yes, R.string.attribute_fuel_no), FOOD(59, -1, "food", R.drawable.attribute_food, R.string.attribute_food_yes, R.string.attribute_food_no), - OC_ONLY(-1, -1, "oc_only", R.drawable.attribute_oc_only, R.string.attribute_oc_only_yes, R.string.attribute_oc_only_no), + OC_ONLY(-1, 1, "oc_only", R.drawable.attribute_oc_only, R.string.attribute_oc_only_yes, R.string.attribute_oc_only_no), LINK_ONLY(-1, -1, "link_only", R.drawable.attribute_link_only, R.string.attribute_link_only_yes, R.string.attribute_link_only_no), LETTERBOX(-1, 4, "letterbox", R.drawable.attribute_letterbox, R.string.attribute_letterbox_yes, R.string.attribute_letterbox_no), RAILWAY(-1, 60, "railway", R.drawable.attribute_railway, R.string.attribute_railway_yes, R.string.attribute_railway_no), @@ -109,7 +109,11 @@ public enum CacheAttribute { OTHER_CACHE(-1, 13, "other_cache", R.drawable.attribute_other_cache, R.string.attribute_other_cache_yes, R.string.attribute_other_cache_no), ASK_OWNER(-1, 17, "ask_owner", R.drawable.attribute_ask_owner, R.string.attribute_ask_owner_yes, R.string.attribute_ask_owner_no), UNKNOWN(-1, -1, "unknown", R.drawable.attribute_unknown, R.string.attribute_unknown_yes, R.string.attribute_unknown_no), - GEOTOUR(67, -1, "geotour", R.drawable.attribute_geotour, R.string.attribute_geotour_yes, R.string.attribute_geotour_no); + GEOTOUR(67, -1, "geotour", R.drawable.attribute_geotour, R.string.attribute_geotour_yes, R.string.attribute_geotour_no), + KIDS_2(-1, 70, "kids_2", R.drawable.attribute_kids_2, R.string.attribute_kids_2_yes, R.string.attribute_kids_2_no), + HISTORIC_SITE(-1, 29, "historic_site", R.drawable.attribute_historic_site, R.string.attribute_historic_site_yes, R.string.attribute_historic_site_no), + MAGNETIC(-1, 6, "magnetic", R.drawable.attribute_magnetic, R.string.attribute_magnetic_yes, R.string.attribute_magnetic_no), + USB_CACHE(-1, 10, "usb_cache", R.drawable.attribute_usb_cache, R.string.attribute_usb_cache_yes, R.string.attribute_usb_cache_no); // THIS LIST IS GENERATED: don't change anything here but read // project/attributes/readme.txt diff --git a/main/src/cgeo/geocaching/enumerations/CacheSize.java b/main/src/cgeo/geocaching/enumerations/CacheSize.java index a6f8df3..ee42c66 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheSize.java +++ b/main/src/cgeo/geocaching/enumerations/CacheSize.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; -import cgeo.geocaching.R; import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; import java.util.Collections; import java.util.HashMap; @@ -56,6 +56,28 @@ public enum CacheSize { if (resultNormalized != null) { return resultNormalized; } + return getByNumber(id); + } + + /** + * Bad GPX files can contain the container size encoded as number. + * + * @param id + * @return + */ + private static CacheSize getByNumber(final String id) { + try { + int numerical = Integer.parseInt(id); + if (numerical != 0) { + for (CacheSize size : CacheSize.values()) { + if (size.comparable == numerical) { + return size; + } + } + } + } catch (NumberFormatException e) { + // ignore, as this might be a number or not + } return UNKNOWN; } diff --git a/main/src/cgeo/geocaching/enumerations/StatusCode.java b/main/src/cgeo/geocaching/enumerations/StatusCode.java index 102b9e9..e1a1aaa 100644 --- a/main/src/cgeo/geocaching/enumerations/StatusCode.java +++ b/main/src/cgeo/geocaching/enumerations/StatusCode.java @@ -11,6 +11,7 @@ public enum StatusCode { LOG_SAVED(R.string.info_log_saved), LOGIN_PARSE_ERROR(R.string.err_parse), CONNECTION_FAILED(R.string.err_server), + CONNECTION_FAILED_EC(R.string.err_server_ec), NO_LOGIN_INFO_STORED(R.string.err_login), UNKNOWN_ERROR(R.string.err_unknown), COMMUNICATION_ERROR(R.string.err_comm), @@ -21,6 +22,7 @@ public enum StatusCode { PREMIUM_ONLY(R.string.err_premium_only), MAINTENANCE(R.string.err_maintenance), LOG_POST_ERROR(R.string.err_log_post_failed), + LOG_POST_ERROR_EC(R.string.err_log_post_failed_ec), NO_LOG_TEXT(R.string.warn_log_text_fill), NO_DATA_FROM_SERVER(R.string.err_log_failed_server), NOT_LOGGED_IN(R.string.init_login_popup_failed), diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index 1ae97f3..4da480a 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -5,7 +5,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; @@ -175,30 +175,30 @@ class FieldnoteExport extends AbstractExport { if (upload) { publishProgress(STATUS_UPLOAD); - if (!Login.isActualLoginStatus()) { + if (!GCLogin.getInstance().isActualLoginStatus()) { // no need to upload (possibly large file) if we're not logged in - final StatusCode loginState = Login.login(); + final StatusCode loginState = GCLogin.getInstance().login(); if (loginState != StatusCode.NO_ERROR) { Log.e("FieldnoteExport.ExportTask upload: Login failed"); } } final String uri = "http://www.geocaching.com/my/uploadfieldnotes.aspx"; - final String page = Login.getRequestLogged(uri, null); + final String page = GCLogin.getInstance().getRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("FieldnoteExport.ExportTask get page: No data from server"); return false; } - final String[] viewstates = Login.getViewstates(page); + final String[] viewstates = GCLogin.getViewstates(page); final Parameters uploadParams = new Parameters( "__EVENTTARGET", "", "__EVENTARGUMENT", "", "ctl00$ContentBody$btnUpload", "Upload Field Note"); - Login.putViewstates(uploadParams, viewstates); + GCLogin.putViewstates(uploadParams, viewstates); Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$FieldNoteLoader", "text/plain", exportFile)); diff --git a/main/src/cgeo/geocaching/files/GPXImporter.java b/main/src/cgeo/geocaching/files/GPXImporter.java index bf0aa72..07ef285 100644 --- a/main/src/cgeo/geocaching/files/GPXImporter.java +++ b/main/src/cgeo/geocaching/files/GPXImporter.java @@ -1,14 +1,15 @@ package cgeo.geocaching.files; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; import cgeo.geocaching.StaticMapsProvider; -import cgeo.geocaching.DataStore; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; @@ -417,19 +418,19 @@ public class GPXImporter { progressHandler.cancel(); final StringBuilder bufferSkipped = new StringBuilder(20); bufferSkipped.append(res.getString(R.string.gpx_import_static_maps_skipped)).append(", ").append(msg.arg1).append(' ').append(res.getString(R.string.gpx_import_caches_imported)); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.gpx_import_title_caches_imported), bufferSkipped.toString()); + Dialogs.message(fromActivity, R.string.gpx_import_title_caches_imported, bufferSkipped.toString()); importFinished(); break; case IMPORT_STEP_FINISHED: progress.dismiss(); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.gpx_import_title_caches_imported), msg.arg1 + " " + res.getString(R.string.gpx_import_caches_imported)); + Dialogs.message(fromActivity, R.string.gpx_import_title_caches_imported, msg.arg1 + " " + res.getString(R.string.gpx_import_caches_imported)); importFinished(); break; case IMPORT_STEP_FINISHED_WITH_ERROR: progress.dismiss(); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.gpx_import_title_caches_import_failed), res.getString(msg.arg1) + "\n\n" + msg.obj); + Dialogs.message(fromActivity, R.string.gpx_import_title_caches_import_failed, res.getString(msg.arg1) + "\n\n" + msg.obj); importFinished(); break; diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index 3e96291..d26a48c 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -104,6 +104,10 @@ public abstract class GPXParser extends FileParser { */ private final Set<String> result = new HashSet<String>(100); private ProgressInputStream progressStream; + /** + * URL contained in the header of the GPX file. Used to guess where the file is coming from. + */ + protected String scriptUrl; private final class UserDataListener implements EndTextElementListener { private final int index; @@ -265,6 +269,14 @@ public abstract class GPXParser extends FileParser { final RootElement root = new RootElement(namespace, "gpx"); final Element waypoint = root.getChild(namespace, "wpt"); + root.getChild(namespace, "url").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String body) { + scriptUrl = body; + } + }); + // waypoint - attributes waypoint.setStartElementListener(new StartElementListener() { @@ -338,7 +350,12 @@ public abstract class GPXParser extends FileParser { if (cache.getName().length() > 2 || StringUtils.isNotBlank(parentCacheCode)) { if (StringUtils.isBlank(parentCacheCode)) { - parentCacheCode = "GC" + cache.getName().substring(2).toUpperCase(Locale.US); + if (StringUtils.containsIgnoreCase(scriptUrl, "extremcaching")) { + parentCacheCode = cache.getName().substring(2); + } + else { + parentCacheCode = "GC" + cache.getName().substring(2).toUpperCase(Locale.US); + } } // lookup cache for waypoint in already parsed caches final Geocache cacheForWaypoint = DataStore.loadCache(parentCacheCode, LoadFlags.LOAD_CACHE_OR_DB); @@ -382,14 +399,19 @@ public abstract class GPXParser extends FileParser { } }); - // waypoint.getName() + // waypoint.name waypoint.getChild(namespace, "name").setEndTextElementListener(new EndTextElementListener() { @Override public void end(String body) { name = body; - final String content = body.trim(); + String content = body.trim(); + + // extremcaching.com manipulates the GC code by adding GC in front of ECxxx + if (StringUtils.startsWithIgnoreCase(content, "GCEC") && StringUtils.containsIgnoreCase(scriptUrl, "extremcaching")) { + content = content.substring(2); + } cache.setName(content); findGeoCode(cache.getName()); @@ -834,6 +856,9 @@ public abstract class GPXParser extends FileParser { } /** + * Overwrite this method in a GPX parser sub class to modify the {@link Geocache}, after it has been fully parsed + * from the GPX file and before it gets stored. + * * @param cache * currently imported cache */ diff --git a/main/src/cgeo/geocaching/files/SimpleDirChooser.java b/main/src/cgeo/geocaching/files/SimpleDirChooser.java index 3f6182c..3e09cc4 100644 --- a/main/src/cgeo/geocaching/files/SimpleDirChooser.java +++ b/main/src/cgeo/geocaching/files/SimpleDirChooser.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** @@ -141,7 +142,7 @@ public class SimpleDirChooser extends AbstractListActivity { } } catch (RuntimeException e) { } - Collections.sort(listDirs); + Collections.sort(listDirs, Option.NAME_COMPARATOR); if (dir.getParent() != null) { listDirs.add(0, new Option(PARENT_DIR, dir.getParent(), false)); } @@ -247,15 +248,21 @@ public class SimpleDirChooser extends AbstractListActivity { } } - /** - * Note: this class has a natural ordering that is inconsistent with equals. - */ - public static class Option implements Comparable<Option> { + public static class Option { private final String name; private final String path; private boolean checked = false; private boolean writeable = false; + private static Comparator<Option> NAME_COMPARATOR = new Comparator<SimpleDirChooser.Option>() { + + @Override + public int compare(Option lhs, Option rhs) { + return String.CASE_INSENSITIVE_ORDER.compare(lhs.name, rhs.name); + } + + }; + public Option(String name, String path, boolean writeable) { this.name = name; this.path = path; @@ -281,14 +288,6 @@ public class SimpleDirChooser extends AbstractListActivity { public boolean isWriteable() { return writeable; } - - @Override - public int compareTo(Option other) { - if (other != null && this.name != null) { - return String.CASE_INSENSITIVE_ORDER.compare(this.name, other.getName()); - } - throw new IllegalArgumentException(""); - } } public static class DirOnlyFilenameFilter implements FilenameFilter { diff --git a/main/src/cgeo/geocaching/filter/TerrainFilter.java b/main/src/cgeo/geocaching/filter/TerrainFilter.java index d74f954..f14313c 100644 --- a/main/src/cgeo/geocaching/filter/TerrainFilter.java +++ b/main/src/cgeo/geocaching/filter/TerrainFilter.java @@ -20,7 +20,7 @@ class TerrainFilter extends AbstractRangeFilter { public static class Factory implements IFilterFactory { private static final int TERRAIN_MIN = 1; - private static final int TERRAIN_MAX = 5; + private static final int TERRAIN_MAX = 7; @Override public List<IFilter> getFilters() { diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index b245aa9..fd4b914 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -11,8 +11,10 @@ import cgeo.geocaching.utils.MatcherWrapper; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -219,19 +221,12 @@ public final class GCVote { return result != null && result.trim().equalsIgnoreCase("ok"); } - public static void loadRatings(final ArrayList<Geocache> caches) { + public static void loadRatings(final @NonNull ArrayList<Geocache> caches) { if (!Settings.isRatingWanted()) { return; } - final ArrayList<String> geocodes = new ArrayList<String>(caches.size()); - for (final Geocache cache : caches) { - String geocode = cache.getGeocode(); - if (StringUtils.isNotBlank(geocode)) { - geocodes.add(geocode); - } - } - + final ArrayList<String> geocodes = getVotableGeocodes(caches); if (geocodes.isEmpty()) { return; } @@ -256,6 +251,24 @@ public final class GCVote { } } + /** + * Get geocodes of all the caches, which can be used with GCVote. Non-GC caches will be filtered out. + * + * @param caches + * @return + */ + private static @NonNull + ArrayList<String> getVotableGeocodes(final @NonNull Collection<Geocache> caches) { + final ArrayList<String> geocodes = new ArrayList<String>(caches.size()); + for (final Geocache cache : caches) { + String geocode = cache.getGeocode(); + if (StringUtils.isNotBlank(geocode) && cache.supportsGCVote()) { + geocodes.add(geocode); + } + } + return geocodes; + } + public static boolean isValidRating(final float rating) { return rating >= MIN_RATING && rating <= MAX_RATING; } diff --git a/main/src/cgeo/geocaching/loaders/AbstractSearchLoader.java b/main/src/cgeo/geocaching/loaders/AbstractSearchLoader.java index 1cc9706..0ba8932 100644 --- a/main/src/cgeo/geocaching/loaders/AbstractSearchLoader.java +++ b/main/src/cgeo/geocaching/loaders/AbstractSearchLoader.java @@ -68,11 +68,6 @@ public abstract class AbstractSearchLoader extends AsyncTaskLoader<SearchResult> } @Override - public boolean takeContentChanged() { - return super.takeContentChanged(); - } - - @Override protected void onStartLoading() { forceLoad(); } @@ -91,7 +86,9 @@ public abstract class AbstractSearchLoader extends AsyncTaskLoader<SearchResult> @Override public synchronized void waitForUser() { try { - wait(); + while (getText() == null) { + wait(); + } } catch (InterruptedException e) { Log.w("searchThread is not waiting for user…"); } diff --git a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java index 34b3a61..3874b47 100644 --- a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java @@ -1,18 +1,18 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.capability.ISearchByCenter; -import cgeo.geocaching.connector.gc.GCParser; import cgeo.geocaching.geopoint.Geopoint; +import org.eclipse.jdt.annotation.NonNull; + import android.content.Context; public class CoordsGeocacheListLoader extends AbstractSearchLoader { - private final Geopoint coords; + private final @NonNull Geopoint coords; - public CoordsGeocacheListLoader(Context context, Geopoint coords) { + public CoordsGeocacheListLoader(final Context context, final @NonNull Geopoint coords) { super(context); this.coords = coords; } @@ -21,13 +21,10 @@ public class CoordsGeocacheListLoader extends AbstractSearchLoader { public SearchResult runSearch() { SearchResult search = new SearchResult(); - if (Settings.isGCConnectorActive()) { - search = GCParser.searchByCoords(coords, Settings.getCacheType(), Settings.isShowCaptcha(), this); - } for (ISearchByCenter centerConn : ConnectorFactory.getSearchByCenterConnectors()) { - if (centerConn.isActivated()) { - search.addSearchResult(centerConn.searchByCenter(coords)); + if (centerConn.isActive()) { + search.addSearchResult(centerConn.searchByCenter(coords, this)); } } diff --git a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java index c8132e7..9c16ee4 100644 --- a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java @@ -3,16 +3,16 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.capability.ISearchByKeyword; -import cgeo.geocaching.connector.gc.GCParser; -import cgeo.geocaching.settings.Settings; + +import org.eclipse.jdt.annotation.NonNull; import android.content.Context; public class KeywordGeocacheListLoader extends AbstractSearchLoader { - private final String keyword; + private final @NonNull String keyword; - public KeywordGeocacheListLoader(Context context, String keyword) { + public KeywordGeocacheListLoader(Context context, final @NonNull String keyword) { super(context); this.keyword = keyword; } @@ -20,13 +20,10 @@ public class KeywordGeocacheListLoader extends AbstractSearchLoader { @Override public SearchResult runSearch() { SearchResult searchResult = new SearchResult(); - if (Settings.isGCConnectorActive()) { - searchResult = GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), this); - } for (ISearchByKeyword connector : ConnectorFactory.getSearchByKeywordConnectors()) { - if (connector.isActivated()) { - searchResult.addSearchResult(connector.searchByName(keyword)); + if (connector.isActive()) { + searchResult.addSearchResult(connector.searchByKeyword(keyword, this)); } } diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 28e1b71..927ff28 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -11,7 +11,8 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.Waypoint; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; @@ -138,7 +139,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // status data /** Last search result used for displaying header */ private SearchResult lastSearchResult = null; - private String[] tokens = null; + private MapTokens tokens = null; private boolean noMapTokenShowed = false; // map status data private boolean followMyLocation = false; @@ -153,8 +154,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private volatile boolean downloaded = false; // overlays private CachesOverlay overlayCaches = null; - private ScaleOverlay overlayScale = null; - private PositionOverlay overlayPosition = null; + private PositionAndScaleOverlay overlayPositionAndScale = null; // data for overlays private static final int[][] INSET_RELIABLE = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; // center, 33x40 / 45x51 / 60x68 private static final int[][] INSET_TYPE = { { 5, 8, 6, 10 }, { 4, 4, 5, 11 }, { 4, 4, 5, 11 } }; // center, 22x22 / 36x36 @@ -347,8 +347,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto outState.putInt(BUNDLE_MAP_SOURCE, currentSourceId); outState.putIntArray(BUNDLE_MAP_STATE, currentMapState()); outState.putBoolean(BUNDLE_LIVE_ENABLED, isLiveEnabled); - if (overlayPosition != null) { - outState.putParcelableArrayList(BUNDLE_TRAIL_HISTORY, overlayPosition.getHistory()); + if (overlayPositionAndScale != null) { + outState.putParcelableArrayList(BUNDLE_TRAIL_HISTORY, overlayPositionAndScale.getHistory()); } } @@ -429,17 +429,13 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto overlayCaches = mapView.createAddMapOverlay(mapView.getContext(), getResources().getDrawable(R.drawable.marker)); } - if (overlayPosition == null) { - overlayPosition = mapView.createAddPositionOverlay(activity); + if (overlayPositionAndScale == null) { + overlayPositionAndScale = mapView.createAddPositionAndScaleOverlay(activity); if (trailHistory != null) { - overlayPosition.setHistory(trailHistory); + overlayPositionAndScale.setHistory(trailHistory); } } - if (overlayScale == null) { - overlayScale = mapView.createAddScaleOverlay(activity); - } - mapView.repaintRequired(null); mapView.getMapController().setZoom(Settings.getMapZoom()); @@ -517,13 +513,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto @Override public void onPause() { - if (loadTimer != null) { - loadTimer.stopIt(); - loadTimer = null; - } - + stopTimer(); deleteGeoDirObservers(); - savePrefs(); if (mapView != null) { @@ -622,7 +613,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto switch (id) { case R.id.menu_trail_mode: Settings.setMapTrail(!Settings.isMapTrail()); - mapView.repaintRequired(overlayPosition); + mapView.repaintRequired(overlayPositionAndScale); ActivityMixin.invalidateOptionsMenu(activity); return true; case R.id.menu_map_live: @@ -929,8 +920,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto try { if (mapView != null) { - if (overlayPosition == null) { - overlayPosition = mapView.createAddPositionOverlay(activity); + if (overlayPositionAndScale == null) { + overlayPositionAndScale = mapView.createAddPositionAndScaleOverlay(activity); } boolean needsRepaintForDistance = needsRepaintForDistance(); @@ -943,9 +934,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } if (needsRepaintForDistance || needsRepaintForHeading) { - overlayPosition.setCoordinates(currentLocation); - overlayPosition.setHeading(currentHeading); - mapView.repaintRequired(overlayPosition); + overlayPositionAndScale.setCoordinates(currentLocation); + overlayPositionAndScale.setHeading(currentHeading); + mapView.repaintRequired(overlayPositionAndScale); } } } catch (RuntimeException e) { @@ -955,7 +946,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } boolean needsRepaintForHeading() { - return Math.abs(AngleUtils.difference(currentHeading, overlayPosition.getHeading())) > MIN_HEADING_DELTA; + return Math.abs(AngleUtils.difference(currentHeading, overlayPositionAndScale.getHeading())) > MIN_HEADING_DELTA; } boolean needsRepaintForDistance() { @@ -964,7 +955,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto return false; } - final Location lastLocation = overlayPosition.getCoordinates(); + final Location lastLocation = overlayPositionAndScale.getCoordinates(); float dist = Float.MAX_VALUE; if (lastLocation != null) { @@ -994,15 +985,19 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto (new DisplayPointThread()).start(); } else { // start timer - if (loadTimer != null) { - loadTimer.stopIt(); - loadTimer = null; - } + stopTimer(); loadTimer = new LoadTimer(); loadTimer.start(); } } + private synchronized void stopTimer() { + if (loadTimer != null) { + loadTimer.stopIt(); + loadTimer = null; + } + } + /** * loading timer Triggers every 250ms and checks for viewport change and starts a {@link LoadRunnable}. */ @@ -1172,8 +1167,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto do { if (tokens == null) { - tokens = Login.getMapTokens(); - if (noMapTokenHandler != null && tokens == null) { + tokens = GCLogin.getMapTokens(); + if (noMapTokenHandler != null && (StringUtils.isEmpty(tokens.getUserSession()) || StringUtils.isEmpty(tokens.getSessionToken()))) { noMapTokenHandler.sendEmptyMessage(0); } } @@ -1181,7 +1176,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens); downloaded = true; if (searchResult.getError() == StatusCode.NOT_LOGGED_IN && Settings.isGCConnectorActive()) { - Login.login(); + GCLogin.getInstance().login(); tokens = null; } else { break; @@ -1311,9 +1306,6 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto protected DoRunnable(final Viewport viewport) { this.viewport = viewport; } - - @Override - public abstract void run(); } /** @@ -1429,7 +1421,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } } - private static void filter(Collection<Geocache> caches) { + private static synchronized void filter(Collection<Geocache> caches) { boolean excludeMine = Settings.isExcludeMyCaches(); boolean excludeDisabled = Settings.isExcludeDisabledCaches(); @@ -1480,7 +1472,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapController.setCenter(mapItemFactory.getGeoPointBase(new Geopoint(mapState[0] / 1.0e6, mapState[1] / 1.0e6))); mapController.setZoom(mapState[2]); } catch (RuntimeException e) { - // nothing at all + Log.e("centermap", e); } centered = true; @@ -1504,7 +1496,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapController.zoomToSpan((int) (viewport.getLatitudeSpan() * 1e6), (int) (viewport.getLongitudeSpan() * 1e6)); } } catch (RuntimeException e) { - // nothing at all + Log.e("centermap", e); } centered = true; @@ -1513,7 +1505,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto try { mapController.setCenter(makeGeoPoint(coordsCenter)); } catch (Exception e) { - // nothing at all + Log.e("centermap", e); } centered = true; diff --git a/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java b/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java new file mode 100644 index 0000000..6b34b75 --- /dev/null +++ b/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java @@ -0,0 +1,73 @@ +package cgeo.geocaching.maps; + +import cgeo.geocaching.maps.interfaces.GeneralOverlay; +import cgeo.geocaching.maps.interfaces.MapProjectionImpl; +import cgeo.geocaching.maps.interfaces.MapViewImpl; +import cgeo.geocaching.maps.interfaces.OverlayImpl; + +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.Point; +import android.location.Location; + +import java.util.ArrayList; + +public class PositionAndScaleOverlay implements GeneralOverlay { + private OverlayImpl ovlImpl = null; + + PositionDrawer positionDrawer = null; + ScaleDrawer scaleDrawer = null; + + public PositionAndScaleOverlay(Activity activity, OverlayImpl ovlImpl) { + this.ovlImpl = ovlImpl; + positionDrawer = new PositionDrawer(activity); + scaleDrawer = new ScaleDrawer(activity); + } + + public void setCoordinates(Location coordinatesIn) { + positionDrawer.setCoordinates(coordinatesIn); + } + + public Location getCoordinates() { + return positionDrawer.getCoordinates(); + } + + public void setHeading(float bearingNow) { + positionDrawer.setHeading(bearingNow); + } + + public float getHeading() { + return positionDrawer.getHeading(); + } + + @Override + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + + drawInternal(canvas, projection, getOverlayImpl().getMapViewImpl()); + } + + @Override + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + + drawInternal(canvas, mapView.getMapProjection(), mapView); + } + + private void drawInternal(Canvas canvas, MapProjectionImpl projection, MapViewImpl mapView) { + positionDrawer.drawPosition(canvas, projection); + scaleDrawer.drawScale(canvas, mapView); + } + + @Override + public OverlayImpl getOverlayImpl() { + return this.ovlImpl; + } + + public ArrayList<Location> getHistory() { + return positionDrawer.getHistory(); + } + + public void setHistory(ArrayList<Location> history) { + positionDrawer.setHistory(history); + } +} diff --git a/main/src/cgeo/geocaching/maps/PositionOverlay.java b/main/src/cgeo/geocaching/maps/PositionDrawer.java index b371eae..1a5dcaf 100644 --- a/main/src/cgeo/geocaching/maps/PositionOverlay.java +++ b/main/src/cgeo/geocaching/maps/PositionDrawer.java @@ -2,12 +2,9 @@ package cgeo.geocaching.maps; import cgeo.geocaching.R; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapItemFactory; import cgeo.geocaching.maps.interfaces.MapProjectionImpl; -import cgeo.geocaching.maps.interfaces.MapViewImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl; import cgeo.geocaching.settings.Settings; import android.app.Activity; @@ -23,7 +20,8 @@ import android.location.Location; import java.util.ArrayList; -public class PositionOverlay implements GeneralOverlay { +public class PositionDrawer { + private Location coordinates = null; private GeoPointImpl location = null; private float heading = 0f; @@ -39,47 +37,14 @@ public class PositionOverlay implements GeneralOverlay { private PaintFlagsDrawFilter remfil = null; private PositionHistory positionHistory = new PositionHistory(); private Activity activity; - private MapItemFactory mapItemFactory = null; - private OverlayImpl ovlImpl = null; + private MapItemFactory mapItemFactory; - public PositionOverlay(Activity activity, OverlayImpl ovlImpl) { + public PositionDrawer(Activity activity) { this.activity = activity; this.mapItemFactory = Settings.getMapProvider().getMapItemFactory(); - this.ovlImpl = ovlImpl; - } - - public void setCoordinates(Location coordinatesIn) { - coordinates = coordinatesIn; - location = mapItemFactory.getGeoPointBase(new Geopoint(coordinates)); - } - - public Location getCoordinates() { - return coordinates; - } - - public void setHeading(float bearingNow) { - heading = bearingNow; - } - - public float getHeading() { - return heading; - } - - @Override - public void drawOverlayBitmap(Canvas canvas, Point drawPosition, - MapProjectionImpl projection, byte drawZoomLevel) { - - drawInternal(canvas, projection); - } - - @Override - public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { - - drawInternal(canvas, mapView.getMapProjection()); } - private void drawInternal(Canvas canvas, MapProjectionImpl projection) { - + void drawPosition(Canvas canvas, MapProjectionImpl projection) { if (coordinates == null || location == null) { return; } @@ -194,13 +159,6 @@ public class PositionOverlay implements GeneralOverlay { canvas.drawBitmap(arrow, matrix, null); canvas.setDrawFilter(remfil); - - //super.draw(canvas, mapView, shadow); - } - - @Override - public OverlayImpl getOverlayImpl() { - return this.ovlImpl; } public ArrayList<Location> getHistory() { @@ -210,4 +168,22 @@ public class PositionOverlay implements GeneralOverlay { public void setHistory(ArrayList<Location> history) { positionHistory.setHistory(history); } + + public void setHeading(float bearingNow) { + heading = bearingNow; + } + + public float getHeading() { + return heading; + } + + public void setCoordinates(Location coordinatesIn) { + coordinates = coordinatesIn; + location = mapItemFactory.getGeoPointBase(new Geopoint(coordinates)); + } + + public Location getCoordinates() { + return coordinates; + } + } diff --git a/main/src/cgeo/geocaching/maps/ScaleOverlay.java b/main/src/cgeo/geocaching/maps/ScaleDrawer.java index bee6acf..fb46408 100644 --- a/main/src/cgeo/geocaching/maps/ScaleOverlay.java +++ b/main/src/cgeo/geocaching/maps/ScaleDrawer.java @@ -2,11 +2,8 @@ package cgeo.geocaching.maps; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; -import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; -import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -14,46 +11,29 @@ import android.app.Activity; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Point; import android.graphics.Typeface; import android.util.DisplayMetrics; -public class ScaleOverlay implements GeneralOverlay { - +public class ScaleDrawer { private static final double SCALE_WIDTH_FACTOR = 1.0 / 2.5; private Paint scale = null; private Paint scaleShadow = null; private BlurMaskFilter blur = null; private float pixelDensity = 0; - private OverlayImpl ovlImpl = null; - - public ScaleOverlay(Activity activity, OverlayImpl overlayImpl) { - this.ovlImpl = overlayImpl; + public ScaleDrawer(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); pixelDensity = metrics.density; } - @Override - public void drawOverlayBitmap(Canvas canvas, Point drawPosition, - MapProjectionImpl projection, byte drawZoomLevel) { - drawInternal(canvas, getOverlayImpl().getMapViewImpl()); - } - - @Override - public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { - drawInternal(canvas, mapView); - } - static private double keepSignificantDigit(final double distance) { final double scale = Math.pow(10, Math.floor(Math.log10(distance))); return scale * Math.floor(distance / scale); } - private void drawInternal(Canvas canvas, MapViewImpl mapView) { - + void drawScale(Canvas canvas, MapViewImpl mapView) { final double span = mapView.getLongitudeSpan() / 1e6; final GeoPointImpl center = mapView.getMapViewCenter(); @@ -109,8 +89,4 @@ public class ScaleOverlay implements GeneralOverlay { canvas.drawText(String.format(formatString, distanceRound) + " " + scaled.right, (float) (pixels - (10 * pixelDensity)), (bottom - (10 * pixelDensity)), scale); } - @Override - public OverlayImpl getOverlayImpl() { - return ovlImpl; - } } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java index 3cf258e..d02e3c2 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java @@ -2,11 +2,9 @@ package cgeo.geocaching.maps.google; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapControllerImpl; @@ -14,13 +12,15 @@ import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; +import org.apache.commons.lang3.reflect.MethodUtils; + import android.app.Activity; import android.content.Context; import android.graphics.Canvas; @@ -31,6 +31,7 @@ import android.view.GestureDetector.SimpleOnGestureListener; import android.view.Gravity; import android.view.MotionEvent; import android.widget.FrameLayout; +import android.widget.ZoomButtonsController; public class GoogleMapView extends MapView implements MapViewImpl { private GestureDetector gestureDetector; @@ -71,9 +72,14 @@ public class GoogleMapView extends MapView implements MapViewImpl { // Push zoom controls to the right FrameLayout.LayoutParams zoomParams = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); zoomParams.gravity = Gravity.RIGHT; - getZoomButtonsController().getZoomControls().setLayoutParams(zoomParams); + // The call to retrieve the zoom buttons controller is undocumented and works so far on all devices + // supported by Google Play, but fails at least on one Jolla. + final ZoomButtonsController controller = (ZoomButtonsController) MethodUtils.invokeMethod(this, "getZoomButtonsController"); + controller.getZoomControls().setLayoutParams(zoomParams); super.displayZoomControls(takeFocus); + } catch (NoSuchMethodException e) { + Log.w("GoogleMapView.displayZoomControls: unable to explicitly place the zoom buttons"); } catch (Exception e) { Log.e("GoogleMapView.displayZoomControls", e); } @@ -119,19 +125,11 @@ public class GoogleMapView extends MapView implements MapViewImpl { } @Override - public PositionOverlay createAddPositionOverlay(Activity activity) { - - GoogleOverlay ovl = new GoogleOverlay(activity, OverlayType.PositionOverlay); - getOverlays().add(ovl); - return (PositionOverlay) ovl.getBase(); - } - - @Override - public ScaleOverlay createAddScaleOverlay(Activity activity) { + public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { - GoogleOverlay ovl = new GoogleOverlay(activity, OverlayType.ScaleOverlay); + GoogleOverlay ovl = new GoogleOverlay(activity); getOverlays().add(ovl); - return (ScaleOverlay) ovl.getBase(); + return (PositionAndScaleOverlay) ovl.getBase(); } @Override diff --git a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java index bf4f606..e937773 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java @@ -1,7 +1,6 @@ package cgeo.geocaching.maps.google; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OverlayImpl; @@ -17,20 +16,11 @@ import java.util.concurrent.locks.ReentrantLock; public class GoogleOverlay extends Overlay implements OverlayImpl { - private GeneralOverlay overlayBase = null; + private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public GoogleOverlay(Activity activityIn, OverlayType ovlType) { - switch (ovlType) { - case PositionOverlay: - overlayBase = new PositionOverlay(activityIn, this); - break; - case ScaleOverlay: - overlayBase = new ScaleOverlay(activityIn, this); - break; - default: - throw new IllegalArgumentException(); - } + public GoogleOverlay(Activity activityIn) { + overlayBase = new PositionAndScaleOverlay(activityIn, this); } @Override diff --git a/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java b/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java index 5481891..cb7ddc6 100644 --- a/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java +++ b/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java @@ -2,8 +2,7 @@ package cgeo.geocaching.maps.interfaces; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import android.app.Activity; import android.content.Context; @@ -47,9 +46,7 @@ public interface MapViewImpl { CachesOverlay createAddMapOverlay(Context context, Drawable drawable); - ScaleOverlay createAddScaleOverlay(Activity activity); - - PositionOverlay createAddPositionOverlay(Activity activity); + PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity); void setMapSource(); diff --git a/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java b/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java index a17b5fb..0984755 100644 --- a/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java +++ b/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java @@ -6,11 +6,6 @@ package cgeo.geocaching.maps.interfaces; */ public interface OverlayImpl { - public enum OverlayType { - PositionOverlay, - ScaleOverlay - } - void lock(); void unlock(); diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java index dc4e82c..78aa47d 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java @@ -3,8 +3,7 @@ package cgeo.geocaching.maps.mapsforge; import cgeo.geocaching.R; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapControllerImpl; @@ -13,7 +12,6 @@ import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; @@ -111,17 +109,10 @@ public class MapsforgeMapView extends MapView implements MapViewImpl { } @Override - public PositionOverlay createAddPositionOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.PositionOverlay); + public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { + MapsforgeOverlay ovl = new MapsforgeOverlay(activity); getOverlays().add(ovl); - return (PositionOverlay) ovl.getBase(); - } - - @Override - public ScaleOverlay createAddScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.ScaleOverlay); - getOverlays().add(ovl); - return (ScaleOverlay) ovl.getBase(); + return (PositionAndScaleOverlay) ovl.getBase(); } @Override diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java index f61e523..74a8601 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java @@ -1,7 +1,6 @@ package cgeo.geocaching.maps.mapsforge; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OverlayImpl; @@ -18,21 +17,11 @@ import java.util.concurrent.locks.ReentrantLock; public class MapsforgeOverlay extends Overlay implements OverlayImpl { - private GeneralOverlay overlayBase = null; + private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn, OverlayImpl.OverlayType ovlType) { - - switch (ovlType) { - case PositionOverlay: - overlayBase = new PositionOverlay(activityIn, this); - break; - case ScaleOverlay: - overlayBase = new ScaleOverlay(activityIn, this); - break; - default: - throw new IllegalStateException(); - } + public MapsforgeOverlay(Activity activityIn) { + overlayBase = new PositionAndScaleOverlay(activityIn, this); } @Override diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java index a074e70..c741a31 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java @@ -1,11 +1,9 @@ package cgeo.geocaching.maps.mapsforge.v024; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapControllerImpl; @@ -13,7 +11,7 @@ import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import org.mapsforge.android.mapsold.GeoPoint; @@ -102,17 +100,10 @@ public class MapsforgeMapView024 extends MapView implements MapViewImpl { } @Override - public PositionOverlay createAddPositionOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.PositionOverlay); - getOverlays().add(ovl); - return (PositionOverlay) ovl.getBase(); - } - - @Override - public ScaleOverlay createAddScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.ScaleOverlay); + public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { + MapsforgeOverlay ovl = new MapsforgeOverlay(activity); getOverlays().add(ovl); - return (ScaleOverlay) ovl.getBase(); + return (PositionAndScaleOverlay) ovl.getBase(); } @Override diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java index 8c9e0c3..655e0b9 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java @@ -1,7 +1,6 @@ package cgeo.geocaching.maps.mapsforge.v024; -import cgeo.geocaching.maps.PositionOverlay; -import cgeo.geocaching.maps.ScaleOverlay; +import cgeo.geocaching.maps.PositionAndScaleOverlay; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OverlayImpl; @@ -18,21 +17,11 @@ import java.util.concurrent.locks.ReentrantLock; public class MapsforgeOverlay extends Overlay implements OverlayImpl { - private GeneralOverlay overlayBase = null; + private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn, OverlayImpl.OverlayType ovlType) { - - switch (ovlType) { - case PositionOverlay: - overlayBase = new PositionOverlay(activityIn, this); - break; - case ScaleOverlay: - overlayBase = new ScaleOverlay(activityIn, this); - break; - default: - throw new IllegalStateException(); - } + public MapsforgeOverlay(Activity activityIn) { + overlayBase = new PositionAndScaleOverlay(activityIn, this); } @Override diff --git a/main/src/cgeo/geocaching/settings/AbstractAttributeBasedPrefence.java b/main/src/cgeo/geocaching/settings/AbstractAttributeBasedPrefence.java new file mode 100644 index 0000000..1930c17 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/AbstractAttributeBasedPrefence.java @@ -0,0 +1,54 @@ +package cgeo.geocaching.settings; + +import org.eclipse.jdt.annotation.Nullable; + +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.Preference; +import android.util.AttributeSet; + +/** + * Base class for preferences which evaluate their XML attributes for further processing. + * + */ +public abstract class AbstractAttributeBasedPrefence extends Preference { + + public AbstractAttributeBasedPrefence(Context context) { + super(context); + } + + public AbstractAttributeBasedPrefence(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs, 0); + } + + public AbstractAttributeBasedPrefence(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + processAttributes(context, attrs, defStyle); + } + + private void processAttributes(Context context, @Nullable AttributeSet attrs, int defStyle) { + if (attrs == null) { + return; + } + TypedArray types = context.obtainStyledAttributes(attrs, getAttributeNames(), + defStyle, 0); + + processAttributeValues(types); + + types.recycle(); + } + + /** + * Evaluate the attributes which where requested in {@link AbstractAttributeBasedPrefence#getAttributeNames()}. + * + * @param values + */ + protected abstract void processAttributeValues(TypedArray values); + + /** + * @return the names of the attributes you want to read in your preference implementation + */ + protected abstract int[] getAttributeNames(); + +} diff --git a/main/src/cgeo/geocaching/settings/AbstractCheckCredentialsPreference.java b/main/src/cgeo/geocaching/settings/AbstractCheckCredentialsPreference.java new file mode 100644 index 0000000..d3aae5c --- /dev/null +++ b/main/src/cgeo/geocaching/settings/AbstractCheckCredentialsPreference.java @@ -0,0 +1,116 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.ui.dialog.Dialogs; +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public abstract class AbstractCheckCredentialsPreference extends Preference { + + public AbstractCheckCredentialsPreference(Context context) { + super(context); + } + + public AbstractCheckCredentialsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AbstractCheckCredentialsPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected View onCreateView(ViewGroup parent) { + setOnPreferenceClickListener(new LoginCheckClickListener()); + return super.onCreateView(parent); + } + + protected abstract ImmutablePair<String, String> getCredentials(); + + protected abstract Object login(); + + private class LoginCheckClickListener implements OnPreferenceClickListener { + private Resources res; + private SettingsActivity activity; + + private ProgressDialog loginDialog; + @SuppressLint("HandlerLeak") + private Handler logInHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + if (loginDialog != null && loginDialog.isShowing()) { + loginDialog.dismiss(); + } + + if (msg.obj == null || (msg.obj instanceof Drawable)) { + Dialogs.message(activity, R.string.init_login_popup, R.string.init_login_popup_ok, (Drawable) msg.obj); + } else { + Dialogs.message(activity, R.string.init_login_popup, + res.getString(R.string.init_login_popup_failed_reason) + + " " + + ((StatusCode) msg.obj).getErrorString(res) + + "."); + } + } catch (Exception e) { + ActivityMixin.showToast(activity, R.string.err_login_failed); + Log.e("SettingsActivity.logInHandler", e); + } finally { + if (loginDialog != null && loginDialog.isShowing()) { + loginDialog.dismiss(); + } + // enable/disable basic member preferences + activity.initBasicMemberPreferences(); + } + } + }; + + @Override + public boolean onPreferenceClick(Preference preference) { + this.activity = (SettingsActivity) AbstractCheckCredentialsPreference.this.getContext(); + this.res = activity.getResources(); + + ImmutablePair<String, String> credentials = getCredentials(); + + // check credentials for validity + if (StringUtils.isBlank(credentials.getLeft()) + || StringUtils.isBlank(credentials.getRight())) { + ActivityMixin.showToast(activity, R.string.err_missing_auth); + return false; + } + + loginDialog = ProgressDialog.show(activity, + res.getString(R.string.init_login_popup), + res.getString(R.string.init_login_popup_working), true); + loginDialog.setCancelable(false); + Cookies.clearCookies(); + + (new Thread() { + @Override + public void run() { + Object payload = login(); + logInHandler.obtainMessage(0, payload).sendToTarget(); + } + }).start(); + + return false; // no shared preference has to be changed + } + } +} diff --git a/main/src/cgeo/geocaching/settings/CapabilitiesPreference.java b/main/src/cgeo/geocaching/settings/CapabilitiesPreference.java new file mode 100644 index 0000000..d2e19b7 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/CapabilitiesPreference.java @@ -0,0 +1,91 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.IConnector; + +import org.apache.commons.lang3.StringUtils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; + +/** + * Preference for displaying the supported capabilities of an {@link IConnector} implementation. + */ +public class CapabilitiesPreference extends AbstractAttributeBasedPrefence { + + private String connectorCode; + + public CapabilitiesPreference(Context context) { + super(context); + } + + public CapabilitiesPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CapabilitiesPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public View getView(View convertView, ViewGroup parent) { + setOnPreferenceClickListener(new ClickListener()); + return super.getView(convertView, parent); + } + + private final class ClickListener implements OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(final Preference preference) { + WebView htmlView = new WebView(preference.getContext()); + htmlView.loadData(createCapabilitiesMessage(), "text/html", null); + AlertDialog.Builder builder = new AlertDialog.Builder(preference.getContext()); + builder.setView(htmlView) + .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(R.string.settings_features) + .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + builder.create().show(); + return false; + } + } + + public String createCapabilitiesMessage() { + // TODO: this needs a better key for the connectors + IConnector connector = ConnectorFactory.getConnector(connectorCode + "1234"); + if (connector == null) { + return StringUtils.EMPTY; + } + StringBuilder builder = new StringBuilder("<p>" + + CgeoApplication.getInstance().getString(R.string.feature_description) + "<ul>"); + + for (String capability : connector.getCapabilities()) { + builder.append("<li>").append(capability).append("</li>"); + } + + builder.append("</ul></p>"); + return builder.toString(); + } + + @Override + protected void processAttributeValues(TypedArray values) { + connectorCode = values.getString(0); + } + + @Override + protected int[] getAttributeNames() { + return new int[] { R.attr.connector }; + } +} diff --git a/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java b/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java index 4e64b9a..e36e007 100644 --- a/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java +++ b/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java @@ -1,13 +1,10 @@ package cgeo.geocaching.settings; import cgeo.geocaching.R; +import cgeo.geocaching.ui.UrlPopup; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.res.TypedArray; -import android.net.Uri; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.util.AttributeSet; @@ -63,26 +60,7 @@ public class CheckBoxWithPopupPreference extends CheckBoxPreference { if (!(Boolean) newValue) { return true; } - AlertDialog.Builder builder = new AlertDialog.Builder( - preference.getContext()); - builder.setMessage(text) - .setIcon(android.R.drawable.ic_dialog_info) - .setTitle(title) - .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .setNegativeButton(urlButton, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - preference.getContext().startActivity(i); - } - }); - builder.create().show(); + new UrlPopup(preference.getContext()).show(title, text, url, urlButton); return true; } }); diff --git a/main/src/cgeo/geocaching/settings/CheckECCredentialsPreference.java b/main/src/cgeo/geocaching/settings/CheckECCredentialsPreference.java new file mode 100644 index 0000000..46a3661 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/CheckECCredentialsPreference.java @@ -0,0 +1,40 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.connector.ec.ECConnector; +import cgeo.geocaching.connector.ec.ECLogin; +import cgeo.geocaching.enumerations.StatusCode; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import android.content.Context; +import android.util.AttributeSet; + +public class CheckECCredentialsPreference extends AbstractCheckCredentialsPreference { + + public CheckECCredentialsPreference(Context context) { + super(context); + } + + public CheckECCredentialsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CheckECCredentialsPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected ImmutablePair<String, String> getCredentials() { + return Settings.getCredentials(ECConnector.getInstance()); + } + + @Override + protected Object login() { + final StatusCode loginResult = ECLogin.getInstance().login(); + Object payload = loginResult; + if (loginResult == StatusCode.NO_ERROR) { + payload = null; + } + return payload; + } +} diff --git a/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java b/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java index 724ab80..12c8b24 100644 --- a/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java +++ b/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java @@ -1,28 +1,14 @@ package cgeo.geocaching.settings; -import cgeo.geocaching.R; -import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.StatusCode; -import cgeo.geocaching.network.Cookies; -import cgeo.geocaching.utils.Log; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import android.annotation.SuppressLint; -import android.app.ProgressDialog; import android.content.Context; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Message; -import android.preference.Preference; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -public class CheckGcCredentialsPreference extends Preference { +public class CheckGcCredentialsPreference extends AbstractCheckCredentialsPreference { public CheckGcCredentialsPreference(Context context) { super(context); @@ -37,87 +23,18 @@ public class CheckGcCredentialsPreference extends Preference { } @Override - protected View onCreateView(ViewGroup parent) { - setOnPreferenceClickListener(GC_LOGIN_CHECK); - return super.onCreateView(parent); + protected ImmutablePair<String, String> getCredentials() { + return Settings.getGcCredentials(); } - private final GcLoginCheck GC_LOGIN_CHECK = new GcLoginCheck(); - - private class GcLoginCheck implements OnPreferenceClickListener { - private Resources res; - private SettingsActivity activity; - - private ProgressDialog loginDialog; - @SuppressLint("HandlerLeak") - private Handler logInHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - try { - if (loginDialog != null && loginDialog.isShowing()) { - loginDialog.dismiss(); - } - - if (msg.obj == null || (msg.obj instanceof Drawable)) { - ActivityMixin.helpDialog(activity, - res.getString(R.string.init_login_popup), - res.getString(R.string.init_login_popup_ok), - (Drawable) msg.obj); - } else { - ActivityMixin.helpDialog(activity, - res.getString(R.string.init_login_popup), - res.getString(R.string.init_login_popup_failed_reason) - + " " - + ((StatusCode) msg.obj).getErrorString(res) - + "."); - } - } catch (Exception e) { - ActivityMixin.showToast(activity, R.string.err_login_failed); - Log.e("SettingsActivity.logInHandler", e); - } finally { - if (loginDialog != null && loginDialog.isShowing()) { - loginDialog.dismiss(); - } - // enable/disable basic member preferences - activity.initBasicMemberPreferences(); - } - } - }; - - @Override - public boolean onPreferenceClick(Preference preference) { - this.activity = (SettingsActivity) CheckGcCredentialsPreference.this.getContext(); - this.res = activity.getResources(); - - ImmutablePair<String, String> credentials = Settings.getGcLogin(); - - // check credentials for validity - if (StringUtils.isBlank(credentials.getLeft()) - || StringUtils.isBlank(credentials.getRight())) { - ActivityMixin.showToast(activity, R.string.err_missing_auth); - return false; - } - - loginDialog = ProgressDialog.show(activity, - res.getString(R.string.init_login_popup), - res.getString(R.string.init_login_popup_working), true); - loginDialog.setCancelable(false); - Cookies.clearCookies(); - - (new Thread() { - @Override - public void run() { - final StatusCode loginResult = Login.login(); - Object payload = loginResult; - if (loginResult == StatusCode.NO_ERROR) { - Login.detectGcCustomDate(); - payload = Login.downloadAvatarAndGetMemberStatus(); - } - logInHandler.obtainMessage(0, payload).sendToTarget(); - } - }).start(); - - return false; // no shared preference has to be changed + @Override + protected Object login() { + final StatusCode loginResult = GCLogin.getInstance().login(); + Object payload = loginResult; + if (loginResult == StatusCode.NO_ERROR) { + GCLogin.detectGcCustomDate(); + payload = GCLogin.getInstance().downloadAvatarAndGetMemberStatus(); } + return payload; } } diff --git a/main/src/cgeo/geocaching/settings/EditPasswordPreference.java b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java index 20d0250..af07041 100644 --- a/main/src/cgeo/geocaching/settings/EditPasswordPreference.java +++ b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java @@ -1,15 +1,16 @@ package cgeo.geocaching.settings; +import org.apache.commons.lang3.StringUtils; + import android.content.Context; import android.preference.EditTextPreference; import android.util.AttributeSet; /** - * This is just a dummy preference, to be able check for the type. + * Password preference. It will only show a row of asterisks as summary instead of the password. * <p> * Use it exactly as an EditTextPreference - * - * @see SettingsActivity - search for EditPasswordPreference + * */ public class EditPasswordPreference extends EditTextPreference { @@ -25,4 +26,13 @@ public class EditPasswordPreference extends EditTextPreference { super(context, attrs, defStyle); } + @Override + public void setSummary(CharSequence summary) { + if (StringUtils.isBlank(summary)) { + super.setSummary(StringUtils.EMPTY); + } else { + super.setSummary(StringUtils.repeat("\u2022 ", 10)); + } + } + } diff --git a/main/src/cgeo/geocaching/settings/InfoPreference.java b/main/src/cgeo/geocaching/settings/InfoPreference.java index ea740b4..8040a62 100644 --- a/main/src/cgeo/geocaching/settings/InfoPreference.java +++ b/main/src/cgeo/geocaching/settings/InfoPreference.java @@ -1,14 +1,11 @@ package cgeo.geocaching.settings; import cgeo.geocaching.R; +import cgeo.geocaching.ui.UrlPopup; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.res.TypedArray; -import android.net.Uri; import android.preference.Preference; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -17,48 +14,61 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; -public class InfoPreference extends Preference { - - // strings for the popup dialog +/** + * Preference which shows a dialog containing textual explanation. The dialog has two buttons, where one will open a + * hyper link with more detailed information. + * <p> + * The URL for the hyper link and the text are given as custom attributes in the preference XML definition. + * </p> + * + */ +public class InfoPreference extends AbstractAttributeBasedPrefence { + + /** + * Content of the dialog, filled from preferences XML. + */ private String text; + /** + * URL for the second button, filled from preferences XML. + */ private String url; + /** + * text for the second button to open an URL, filled from preferences XML. + */ private String urlButton; private LayoutInflater inflater; public InfoPreference(Context context) { super(context); - init(context, null, 0); + init(context); } public InfoPreference(Context context, AttributeSet attrs) { super(context, attrs); - init(context, attrs, 0); + init(context); } public InfoPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - init(context, attrs, defStyle); + init(context); } - private void init(Context context, AttributeSet attrs, int defStyle) { + private void init(Context context) { inflater = ((Activity) context).getLayoutInflater(); - setPersistent(false); + } - if (attrs == null) { - return; // coward's retreat - } - - TypedArray types = context.obtainStyledAttributes(attrs, new int[] { - android.R.attr.text, R.attr.url, R.attr.urlButton }, - defStyle, 0); - - text = types.getString(0); - url = types.getString(1); - urlButton = types.getString(2); + @Override + protected int[] getAttributeNames() { + return new int[] { android.R.attr.text, R.attr.url, R.attr.urlButton }; + } - types.recycle(); + @Override + protected void processAttributeValues(TypedArray values) { + text = values.getString(0); + url = values.getString(1); + urlButton = values.getString(2); } @Override @@ -69,39 +79,30 @@ public class InfoPreference extends Preference { @Override public boolean onPreferenceClick(final Preference preference) { - AlertDialog.Builder builder = new AlertDialog.Builder( - preference.getContext()); - builder.setMessage(text) - .setIcon(android.R.drawable.ic_dialog_info) - .setTitle(preference.getTitle()) - .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .setNegativeButton(urlButton, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - preference.getContext().startActivity(i); - } - }); - builder.create().show(); + new UrlPopup(preference.getContext()).show(preference.getTitle().toString(), text, url, urlButton); + // don't update the preference value return false; } }); - // show an Info Icon - View v = super.onCreateView(parent); - - ImageView i = (ImageView) inflater.inflate(R.layout.preference_info_icon, parent, false); - LinearLayout l = (LinearLayout) v.findViewById(android.R.id.widget_frame); - l.setVisibility(View.VISIBLE); - l.addView(i); + return addInfoIcon(parent); + } - return v; + /** + * Add an info icon at the left hand side of the preference. + * + * @param parent + * @return + */ + private View addInfoIcon(ViewGroup parent) { + View preferenceView = super.onCreateView(parent); + + ImageView iconView = (ImageView) inflater.inflate(R.layout.preference_info_icon, parent, false); + LinearLayout frame = (LinearLayout) preferenceView.findViewById(android.R.id.widget_frame); + frame.setVisibility(View.VISIBLE); + frame.addView(iconView); + + return preferenceView; } } diff --git a/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java index fbf08fa..094b8f5 100644 --- a/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java +++ b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java @@ -4,6 +4,7 @@ import cgeo.geocaching.R; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; @@ -45,14 +46,11 @@ public class RegisterSend2CgeoPreference extends Preference { } if (msg.what > 0) { - ActivityMixin.helpDialog(activity, - activity.getString(R.string.init_sendToCgeo), + Dialogs.message(activity, R.string.init_sendToCgeo, activity.getString(R.string.init_sendToCgeo_register_ok) .replace("####", String.valueOf(msg.what))); } else { - ActivityMixin.helpDialog(activity, - activity.getString(R.string.init_sendToCgeo), - activity.getString(R.string.init_sendToCgeo_register_fail)); + Dialogs.message(activity, R.string.init_sendToCgeo, R.string.init_sendToCgeo_register_fail); } } catch (Exception e) { ActivityMixin.showToast(activity, R.string.init_sendToCgeo_register_fail); diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java index 146182a..186e5d9 100644 --- a/main/src/cgeo/geocaching/settings/Settings.java +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -3,8 +3,10 @@ package cgeo.geocaching.settings; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; +import cgeo.geocaching.connector.capability.ICredentials; +import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.gc.GCConstants; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LogType; @@ -24,6 +26,7 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import android.content.Context; @@ -270,14 +273,22 @@ public class Settings { } /** - * Get login and password information. + * Get login and password information of Geocaching.com. * * @return a pair either with (login, password) or (empty, empty) if no valid information is stored */ - public static ImmutablePair<String, String> getGcLogin() { + public static ImmutablePair<String, String> getGcCredentials() { + return getCredentials(GCConnector.getInstance()); + } - final String username = getString(R.string.pref_username, null); - final String password = getString(R.string.pref_password, null); + /** + * Get login and password information. + * + * @return a pair either with (login, password) or (empty, empty) if no valid information is stored + */ + public static ImmutablePair<String, String> getCredentials(final @NonNull ICredentials connector) { + final String username = getString(connector.getUsernamePreferenceKey(), null); + final String password = getString(connector.getPasswordPreferenceKey(), null); if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { return new ImmutablePair<String, String>(StringUtils.EMPTY, StringUtils.EMPTY); @@ -294,6 +305,14 @@ public class Settings { return getBoolean(R.string.pref_connectorGCActive, true); } + public static boolean isECConnectorActive() { + return getBoolean(R.string.pref_connectorECActive, false); + } + + public static boolean isOXConnectorActive() { + return getBoolean(R.string.pref_connectorOXActive, false); + } + public static boolean isPremiumMember() { // Basic Member, Premium Member, ??? return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(Settings.getMemberStatus()); @@ -468,7 +487,7 @@ public class Settings { /** * @return User selected date format on GC.com - * @see Login#GC_CUSTOM_DATE_FORMATS + * @see GCLogin#GC_CUSTOM_DATE_FORMATS */ public static String getGcCustomDate() { return getString(R.string.pref_gccustomdate, null); @@ -611,8 +630,6 @@ public class Settings { * @return */ private static int getConvertedMapId() { - // what the heck is happening here?? hashCodes of Strings? - // why not strings? final int id = Integer.parseInt(getString(R.string.pref_mapsource, String.valueOf(MAP_SOURCE_DEFAULT))); switch (id) { @@ -925,18 +942,16 @@ public class Settings { putBoolean(R.string.pref_excludemine, exclude); } - static boolean setLogin(final String username, final String password) { - + static void setLogin(final String username, final String password) { if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { // erase username and password - boolean a = remove(R.string.pref_username); - boolean b = remove(R.string.pref_password); - return a && b; + remove(R.string.pref_username); + remove(R.string.pref_password); + return; } // save username and password - boolean a = putString(R.string.pref_username, username); - boolean b = putString(R.string.pref_password, password); - return a && b; + putString(R.string.pref_username, username); + putString(R.string.pref_password, password); } public static long getFieldnoteExportDate() { @@ -982,4 +997,8 @@ public class Settings { return getBoolean(R.string.pref_fieldNoteExportOnlyNew, false); } + public static String getECIconSet() { + return getString(R.string.pref_ec_icons, "1"); + } + } diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index 403b11d..cd9296e 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -9,7 +9,7 @@ import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.gc.GCConnector; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; @@ -128,7 +128,8 @@ public class SettingsActivity extends PreferenceActivity { R.string.pref_gpxExportDir, R.string.pref_gpxImportDir, R.string.pref_mapDirectory, R.string.pref_defaultNavigationTool, R.string.pref_defaultNavigationTool2, R.string.pref_webDeviceName, - R.string.pref_fakekey_preference_backup_info, R.string.pref_twitter_cache_message, R.string.pref_twitter_trackable_message }) { + R.string.pref_fakekey_preference_backup_info, R.string.pref_twitter_cache_message, R.string.pref_twitter_trackable_message, + R.string.pref_ecusername, R.string.pref_ecpassword, R.string.pref_ec_icons }) { bindSummaryToStringValue(k); } getPreference(R.string.pref_units).setDefaultValue(Settings.getImperialUnitsDefault()); @@ -149,9 +150,11 @@ public class SettingsActivity extends PreferenceActivity { getPreference(R.string.pref_connectorOCActive).setOnPreferenceChangeListener(VALUE_CHANGE_LISTENER); getPreference(R.string.pref_connectorOCPLActive).setOnPreferenceChangeListener(VALUE_CHANGE_LISTENER); getPreference(R.string.pref_connectorGCActive).setOnPreferenceChangeListener(VALUE_CHANGE_LISTENER); + getPreference(R.string.pref_connectorECActive).setOnPreferenceChangeListener(VALUE_CHANGE_LISTENER); setWebsite(R.string.pref_fakekey_gc_website, GCConnector.getInstance().getHost()); setWebsite(R.string.pref_fakekey_ocde_website, "opencaching.de"); setWebsite(R.string.pref_fakekey_ocpl_website, "opencaching.pl"); + setWebsite(R.string.pref_fakekey_ec_website, "extremcaching.com"); setWebsite(R.string.pref_fakekey_gcvote_website, "gcvote.com"); setWebsite(R.string.pref_fakekey_sendtocgeo_website, "send2.cgeo.org"); } @@ -466,20 +469,14 @@ public class SettingsActivity extends PreferenceActivity { public boolean onPreferenceChange(final Preference preference, final Object value) { String stringValue = value.toString(); - if (preference instanceof EditPasswordPreference) { - if (StringUtils.isBlank((String) value)) { - preference.setSummary(StringUtils.EMPTY); - } else { - preference.setSummary(StringUtils.repeat("\u2022 ", 10)); - } - } else if (isPreference(preference, R.string.pref_mapsource)) { + if (isPreference(preference, R.string.pref_mapsource)) { // reset the cached map source MapSource mapSource; try { - final int mapSourceId = Integer.valueOf(stringValue); + final int mapSourceId = Integer.parseInt(stringValue); mapSource = MapProviderFactory.getMapSource(mapSourceId); } catch (final NumberFormatException e) { - Log.e("SettingsActivity.onPreferenceChange: bad source id `" + stringValue + "'"); + Log.e("SettingsActivity.onPreferenceChange: bad source id '" + stringValue + "'"); mapSource = null; } // If there is no corresponding map source (because some map sources were @@ -495,7 +492,7 @@ public class SettingsActivity extends PreferenceActivity { } Settings.setMapSource(mapSource); preference.setSummary(mapSource.getName()); - } else if (isPreference(preference, R.string.pref_connectorOCActive) || isPreference(preference, R.string.pref_connectorOCPLActive) || isPreference(preference, R.string.pref_connectorGCActive)) { + } else if (isPreference(preference, R.string.pref_connectorOCActive) || isPreference(preference, R.string.pref_connectorOCPLActive) || isPreference(preference, R.string.pref_connectorGCActive) || isPreference(preference, R.string.pref_connectorECActive)) { // // reset log-in status if connector activation was changed CgeoApplication.getInstance().checkLogin = true; } else if (preference instanceof ListPreference) { @@ -523,10 +520,10 @@ public class SettingsActivity extends PreferenceActivity { // simple string representation. preference.setSummary(stringValue); } - if ((isPreference(preference, R.string.pref_username) && !stringValue.equals(Settings.getUsername())) || (isPreference(preference, R.string.pref_password) && !stringValue.equals(Settings.getGcLogin().getRight()))) { + if ((isPreference(preference, R.string.pref_username) && !stringValue.equals(Settings.getUsername())) || (isPreference(preference, R.string.pref_password) && !stringValue.equals(Settings.getGcCredentials().getRight()))) { // reset log-in if gc user or password is changed - if (Login.isActualLoginStatus()) { - Login.logout(); + if (GCLogin.getInstance().isActualLoginStatus()) { + GCLogin.getInstance().logout(); } CgeoApplication.getInstance().checkLogin = true; } diff --git a/main/src/cgeo/geocaching/settings/TemplateTextPreference.java b/main/src/cgeo/geocaching/settings/TemplateTextPreference.java index 9eaaa67..a703231 100644 --- a/main/src/cgeo/geocaching/settings/TemplateTextPreference.java +++ b/main/src/cgeo/geocaching/settings/TemplateTextPreference.java @@ -48,7 +48,7 @@ public class TemplateTextPreference extends DialogPreference { settingsActivity = (SettingsActivity) this.getContext(); editText = (EditText) view.findViewById(R.id.signature_dialog_text); - editText.setText(getPersistedString(initialValue != null ? initialValue.toString() : StringUtils.EMPTY)); + editText.setText(getPersistedString(initialValue != null ? initialValue : StringUtils.EMPTY)); Button button = (Button) view.findViewById(R.id.signature_templates); button.setOnClickListener(new View.OnClickListener() { diff --git a/main/src/cgeo/geocaching/settings/TextPreference.java b/main/src/cgeo/geocaching/settings/TextPreference.java index bcd03ff..eecf4cc 100644 --- a/main/src/cgeo/geocaching/settings/TextPreference.java +++ b/main/src/cgeo/geocaching/settings/TextPreference.java @@ -4,21 +4,26 @@ import cgeo.geocaching.R; import android.content.Context; import android.content.res.TypedArray; -import android.preference.Preference; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; /** - * Preference to simply show a text message. + * Preference to simply show a text message. Links are not shown. * <p> - * Links are not shown - I tried everything (koem) - * <p> - * example: <cgeo.geocaching.settings.TextPreference android:text="@string/legal_note" - * android:layout="@string/text_preference_default_layout" /> + * Usage: The displayed text is taken from the "android:text" attribute of the preference definition. Example: + * + * <pre> + * <cgeo.geocaching.settings.TextPreference + * android:text="@string/legal_note" + * android:layout="@string/text_preference_default_layout" + * /> + * </pre> + * + * </p> */ -public class TextPreference extends Preference { +public class TextPreference extends AbstractAttributeBasedPrefence { private String text; private TextView summaryView; @@ -30,23 +35,20 @@ public class TextPreference extends Preference { public TextPreference(Context context, AttributeSet attrs) { super(context, attrs); - processAttributes(context, attrs, 0); } public TextPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - processAttributes(context, attrs, defStyle); } - private void processAttributes(Context context, AttributeSet attrs, int defStyle) { - if (attrs == null) { - return; - } + @Override + protected int[] getAttributeNames() { + return new int[] { android.R.attr.text }; + } - TypedArray types = context.obtainStyledAttributes(attrs, new int[] { - android.R.attr.text }, defStyle, 0); - this.text = types.getString(0); - types.recycle(); + @Override + protected void processAttributeValues(TypedArray values) { + this.text = values.getString(0); } @Override @@ -67,20 +69,21 @@ public class TextPreference extends Preference { @Override public void setSummary(CharSequence summaryText) { // the layout hasn't been inflated yet, save the summaryText for later use - if (this.summaryView == null) { + if (summaryView == null) { this.summaryText = summaryText; return; } - // if summaryText is null, take it from the previous saved summary + // if summaryText is null, take it from the previously saved summary if (summaryText == null) { if (this.summaryText == null) { return; } - this.summaryView.setText(this.summaryText); + summaryView.setText(this.summaryText); } else { - this.summaryView.setText(summaryText); + summaryView.setText(summaryText); } this.summaryView.setVisibility(View.VISIBLE); } + } diff --git a/main/src/cgeo/geocaching/settings/WpThresholdPreference.java b/main/src/cgeo/geocaching/settings/WpThresholdPreference.java index 867714f..4c43acf 100644 --- a/main/src/cgeo/geocaching/settings/WpThresholdPreference.java +++ b/main/src/cgeo/geocaching/settings/WpThresholdPreference.java @@ -1,7 +1,6 @@ package cgeo.geocaching.settings; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import android.content.Context; import android.preference.Preference; @@ -14,7 +13,7 @@ import android.widget.TextView; public class WpThresholdPreference extends Preference { - TextView valueView; + private TextView valueView; public WpThresholdPreference(Context context) { super(context); diff --git a/main/src/cgeo/geocaching/sorting/AbstractCacheComparator.java b/main/src/cgeo/geocaching/sorting/AbstractCacheComparator.java index 2dee713..a1c04a4 100644 --- a/main/src/cgeo/geocaching/sorting/AbstractCacheComparator.java +++ b/main/src/cgeo/geocaching/sorting/AbstractCacheComparator.java @@ -45,4 +45,5 @@ public abstract class AbstractCacheComparator implements CacheComparator { * cache2. */ protected abstract int compareCaches(final Geocache cache1, final Geocache cache2); + } diff --git a/main/src/cgeo/geocaching/sorting/FindsComparator.java b/main/src/cgeo/geocaching/sorting/FindsComparator.java index b147fad..c889776 100644 --- a/main/src/cgeo/geocaching/sorting/FindsComparator.java +++ b/main/src/cgeo/geocaching/sorting/FindsComparator.java @@ -1,8 +1,6 @@ package cgeo.geocaching.sorting; -import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; -import cgeo.geocaching.enumerations.LogType; public class FindsComparator extends AbstractCacheComparator { @@ -13,20 +11,9 @@ public class FindsComparator extends AbstractCacheComparator { @Override protected int compareCaches(Geocache cache1, Geocache cache2) { - int finds1 = getFindsCount(cache1); - int finds2 = getFindsCount(cache2); + int finds1 = cache1.getFindsCount(); + int finds2 = cache2.getFindsCount(); return finds2 - finds1; } - private static int getFindsCount(Geocache cache) { - if (cache.getLogCounts().isEmpty()) { - cache.setLogCounts(DataStore.loadLogCounts(cache.getGeocode())); - } - Integer logged = cache.getLogCounts().get(LogType.FOUND_IT); - if (logged != null) { - return logged; - } - return 0; - } - } diff --git a/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java index b5edf17..f438762 100644 --- a/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java +++ b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java @@ -3,9 +3,7 @@ */ package cgeo.geocaching.sorting; -import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; -import cgeo.geocaching.enumerations.LogType; /** * sorts caches by popularity ratio (favorites per find in %). @@ -23,8 +21,8 @@ public class PopularityRatioComparator extends AbstractCacheComparator { float ratio1 = 0.0f; float ratio2 = 0.0f; - int finds1 = getFindsCount(cache1); - int finds2 = getFindsCount(cache2); + int finds1 = cache1.getFindsCount(); + int finds2 = cache2.getFindsCount(); if (finds1 != 0) { ratio1 = (((float) cache1.getFavoritePoints()) / ((float) finds1)); @@ -41,15 +39,4 @@ public class PopularityRatioComparator extends AbstractCacheComparator { return 0; } - - private static int getFindsCount(Geocache cache) { - if (cache.getLogCounts().isEmpty()) { - cache.setLogCounts(DataStore.loadLogCounts(cache.getGeocode())); - } - Integer logged = cache.getLogCounts().get(LogType.FOUND_IT); - if (logged != null) { - return logged; - } - return 0; - } } diff --git a/main/src/cgeo/geocaching/ui/AbstractCachingListViewPageViewCreator.java b/main/src/cgeo/geocaching/ui/AbstractCachingListViewPageViewCreator.java new file mode 100644 index 0000000..799b695 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/AbstractCachingListViewPageViewCreator.java @@ -0,0 +1,57 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.activity.AbstractViewPagerActivity.PageViewCreator; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import android.os.Bundle; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.widget.ListView; + +/** + * {@link PageViewCreator} for {@link ListView}, which can save scroll state on purging a page from the + * {@link ViewPager}, and restore the state on re-recreation. + * + */ +public abstract class AbstractCachingListViewPageViewCreator extends AbstractCachingPageViewCreator<ListView> { + private static final String STATE_POSITION_FROM_TOP = "positionFromTop"; + private static final String STATE_POSITION = "position"; + + /** + * Get the state of the current view + * + * @return the state encapsulated in a bundle + */ + @Override + public @Nullable + Bundle getViewState() { + if (view == null) { + return null; + } + int position = view.getFirstVisiblePosition(); + View child = view.getChildAt(0); + int positionFromTop = (child == null) ? 0 : child.getTop(); + Bundle state = new Bundle(); + state.putInt(STATE_POSITION, position); + state.putInt(STATE_POSITION_FROM_TOP, positionFromTop); + return state; + } + + /** + * Restore a previously stored state of the view + * + */ + @Override + public void setViewState(@NonNull Bundle state) { + if (view == null) { + return; + } + int logViewPosition = state.getInt(STATE_POSITION); + int logViewPositionFromTop = state.getInt(STATE_POSITION_FROM_TOP); + view.setSelectionFromTop(logViewPosition, logViewPositionFromTop); + return; + } + +} diff --git a/main/src/cgeo/geocaching/ui/AbstractCachingPageViewCreator.java b/main/src/cgeo/geocaching/ui/AbstractCachingPageViewCreator.java index 333ef11..ed5d182 100644 --- a/main/src/cgeo/geocaching/ui/AbstractCachingPageViewCreator.java +++ b/main/src/cgeo/geocaching/ui/AbstractCachingPageViewCreator.java @@ -2,6 +2,12 @@ package cgeo.geocaching.ui; import cgeo.geocaching.activity.AbstractViewPagerActivity.PageViewCreator; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import android.os.Bundle; import android.view.View; /** @@ -28,5 +34,25 @@ public abstract class AbstractCachingPageViewCreator<ViewClass extends View> imp } @Override + @SuppressFBWarnings("USM_USELESS_ABSTRACT_METHOD") public abstract ViewClass getDispatchedView(); + + /** + * Gets the state of the view but returns an empty state if not overridden + * + * @return empty bundle + */ + @Override + public @Nullable + Bundle getViewState() { + return new Bundle(); + } + + /** + * Restores the state of the view but just returns if not overridden. + */ + @Override + public void setViewState(@NonNull Bundle state) { + return; + } } diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java index 0d5fba7..8134235 100644 --- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java +++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java @@ -11,7 +11,6 @@ import cgeo.geocaching.geopoint.Units; import org.apache.commons.lang3.StringUtils; import android.app.Activity; -import android.content.Context; import android.location.Address; import android.view.LayoutInflater; import android.view.View; @@ -35,9 +34,9 @@ public class AddressListAdapter extends ArrayAdapter<Address> { } } - public AddressListAdapter(final Context context) { + public AddressListAdapter(final Activity context) { super(context, 0); - inflater = ((Activity) context).getLayoutInflater(); + inflater = context.getLayoutInflater(); location = CgeoApplication.getInstance().currentGeo().getCoords(); } diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java index f1cee05..7fe77c4 100644 --- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java +++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java @@ -4,6 +4,7 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; +import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; @@ -53,24 +54,28 @@ public final class CacheDetailsCreator { } public RelativeLayout addStars(final int nameId, final float value) { + return addStars(nameId, value, 5); + } + + public RelativeLayout addStars(final int nameId, final float value, final int max) { final RelativeLayout layout = (RelativeLayout) activity.getLayoutInflater().inflate(R.layout.cache_information_item, null); final TextView nameView = (TextView) layout.findViewById(R.id.name); lastValueView = (TextView) layout.findViewById(R.id.value); final LinearLayout layoutStars = (LinearLayout) layout.findViewById(R.id.stars); nameView.setText(activity.getResources().getString(nameId)); - lastValueView.setText(String.format("%.1f", value) + ' ' + activity.getResources().getString(R.string.cache_rating_of) + " 5"); - createStarImages(layoutStars, value); + lastValueView.setText(String.format("%.1f", value) + ' ' + activity.getResources().getString(R.string.cache_rating_of) + " " + String.format("%d", max)); + createStarImages(layoutStars, value, max); layoutStars.setVisibility(View.VISIBLE); parentView.addView(layout); return layout; } - private void createStarImages(final ViewGroup starsContainer, final float value) { + private void createStarImages(final ViewGroup starsContainer, final float value, final int max) { final LayoutInflater inflater = LayoutInflater.from(activity); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < max; i++) { ImageView star = (ImageView) inflater.inflate(R.layout.star_image, null); if (value - i >= 0.75) { star.setImageResource(R.drawable.star_on); @@ -130,7 +135,7 @@ public final class CacheDetailsCreator { public void addTerrain(Geocache cache) { if (cache.getTerrain() > 0) { - addStars(R.string.cache_terrain, cache.getTerrain()); + addStars(R.string.cache_terrain, cache.getTerrain(), ConnectorFactory.getConnector(cache).getMaxTerrain()); } } diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java index 3b4ed36..5e80f32 100644 --- a/main/src/cgeo/geocaching/ui/CompassView.java +++ b/main/src/cgeo/geocaching/ui/CompassView.java @@ -80,7 +80,9 @@ public class CompassView extends View implements PeriodicHandlerListener { setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); - initialDisplay = true; + synchronized (this) { + initialDisplay = true; + } redrawHandler.start(); } @@ -148,7 +150,7 @@ public class CompassView extends View implements PeriodicHandlerListener { } @Override - public void onPeriodic() { + public synchronized void onPeriodic() { final float newAzimuthShown = smoothUpdate(northMeasured, azimuthShown); final float newCacheHeadingShown = smoothUpdate(cacheHeadingMeasured, cacheHeadingShown); if (Math.abs(AngleUtils.difference(azimuthShown, newAzimuthShown)) >= 2 || diff --git a/main/src/cgeo/geocaching/ui/GPXListAdapter.java b/main/src/cgeo/geocaching/ui/GPXListAdapter.java index 988bb81..ae18ab4 100644 --- a/main/src/cgeo/geocaching/ui/GPXListAdapter.java +++ b/main/src/cgeo/geocaching/ui/GPXListAdapter.java @@ -5,11 +5,11 @@ import butterknife.InjectView; import cgeo.geocaching.GpxFileListActivity; import cgeo.geocaching.R; import cgeo.geocaching.files.GPXImporter; +import cgeo.geocaching.ui.dialog.Dialogs; import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.view.LayoutInflater; import android.view.View; @@ -74,25 +74,13 @@ public class GPXListAdapter extends ArrayAdapter<File> { @Override public boolean onLongClick(View v) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.gpx_import_delete_title) - .setMessage(activity.getString(R.string.gpx_import_delete_message, file.getName())) - .setCancelable(false) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - FileUtils.deleteIgnoringFailure(file); - GPXListAdapter.this.remove(file); - } - }) - .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - AlertDialog alert = builder.create(); - alert.show(); + Dialogs.confirmYesNo(activity, R.string.gpx_import_delete_title, activity.getString(R.string.gpx_import_delete_message, file.getName()), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + FileUtils.deleteIgnoringFailure(file); + GPXListAdapter.this.remove(file); + } + }); return true; } }); diff --git a/main/src/cgeo/geocaching/ui/UrlPopup.java b/main/src/cgeo/geocaching/ui/UrlPopup.java new file mode 100644 index 0000000..5a8dba4 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/UrlPopup.java @@ -0,0 +1,40 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.R; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; + +public class UrlPopup { + + private final Context context; + + public UrlPopup(final Context context) { + this.context = context; + } + + public void show(final String title, final String message, final String url, final String urlButtonTitle) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(message) + .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(title) + .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }) + .setNegativeButton(urlButtonTitle, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + context.startActivity(i); + } + }); + builder.create().show(); + } +} diff --git a/main/src/cgeo/geocaching/ui/dialog/Dialogs.java b/main/src/cgeo/geocaching/ui/dialog/Dialogs.java new file mode 100644 index 0000000..6064c41 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/dialog/Dialogs.java @@ -0,0 +1,291 @@ +package cgeo.geocaching.ui.dialog; + +import cgeo.geocaching.CgeoApplication; + +import org.eclipse.jdt.annotation.Nullable; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.DialogInterface.OnClickListener; +import android.graphics.drawable.Drawable; + +/** + * Wrapper for {@link AlertDialog}. If you want to show a simple text, use one of the + * {@link #message(Activity, String, String, Drawable)} variants. If you want the user to confirm using Okay/Cancel or + * Yes/No, select one of the {@link #confirm(Activity, String, String, String, OnClickListener)} or + * {@link #confirmYesNo(Activity, String, String, OnClickListener)} variants. + * + */ +public final class Dialogs { + private Dialogs() { + // utility class + } + + /** + * Confirm using two buttons "yourText" and "Cancel", where "Cancel" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param positiveButton + * text of the positive button (which would typically be the OK button) + * @param okayListener + * listener of the positive button + */ + public static AlertDialog.Builder confirm(final Activity context, final String title, final String msg, final String positiveButton, final OnClickListener okayListener) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + AlertDialog dialog = builder.setTitle(title) + .setCancelable(true) + .setMessage(msg) + .setPositiveButton(positiveButton, okayListener) + .setNegativeButton(android.R.string.cancel, null) + .create(); + dialog.setOwnerActivity(context); + dialog.show(); + return builder; + } + + /** + * Confirm using two buttons "yourText" and "Cancel", where "Cancel" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param positiveButton + * text of the positive button (which would typically be the OK button) + * @param okayListener + * listener of the positive button + */ + public static AlertDialog.Builder confirm(final Activity context, final int title, final int msg, final int positiveButton, final OnClickListener okayListener) { + return confirm(context, getString(title), getString(msg), getString(positiveButton), okayListener); + } + + /** + * Confirm using two buttons "Yes" and "No", where "No" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param yesListener + * listener of the positive button + */ + public static AlertDialog.Builder confirmYesNo(final Activity context, final String title, final String msg, final OnClickListener yesListener) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + AlertDialog dialog = builder.setTitle(title) + .setCancelable(true) + .setMessage(msg) + .setPositiveButton(android.R.string.yes, yesListener) + .setNegativeButton(android.R.string.no, null) + .create(); + dialog.setOwnerActivity(context); + dialog.show(); + return builder; + } + + /** + * Confirm using two buttons "Yes" and "No", where "No" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param yesListener + * listener of the positive button + */ + public static AlertDialog.Builder confirmYesNo(final Activity context, final String title, final int msg, final OnClickListener yesListener) { + return confirmYesNo(context, title, getString(msg), yesListener); + } + + /** + * Confirm using two buttons "Yes" and "No", where "No" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param yesListener + * listener of the positive button + */ + public static AlertDialog.Builder confirmYesNo(final Activity context, final int title, final String msg, final OnClickListener yesListener) { + return confirmYesNo(context, getString(title), msg, yesListener); + } + + /** + * Confirm using two buttons "Yes" and "No", where "No" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param yesListener + * listener of the positive button + */ + public static AlertDialog.Builder confirmYesNo(final Activity context, final int title, final int msg, final OnClickListener yesListener) { + return confirmYesNo(context, getString(title), getString(msg), yesListener); + } + + /** + * Confirm using two buttons "OK" and "Cancel", where "Cancel" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param okayListener + * listener of the positive button + */ + public static AlertDialog.Builder confirm(final Activity context, final String title, final String msg, final OnClickListener okayListener) { + return confirm(context, title, msg, getString(android.R.string.ok), okayListener); + } + + /** + * Confirm using two buttons "OK" and "Cancel", where "Cancel" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param okayListener + * listener of the positive button + */ + public static AlertDialog.Builder confirm(final Activity context, final int title, final String msg, final OnClickListener okayListener) { + return confirm(context, getString(title), msg, okayListener); + } + + /** + * Confirm using two buttons "OK" and "Cancel", where "Cancel" just closes the dialog. + * + * @param context + * activity hosting the dialog + * @param title + * dialog title + * @param msg + * dialog message + * @param okayListener + * listener of the positive button + */ + public static AlertDialog.Builder confirm(final Activity context, final int title, final int msg, final OnClickListener okayListener) { + return confirm(context, getString(title), getString(msg), okayListener); + } + + private static String getString(int resourceId) { + return CgeoApplication.getInstance().getString(resourceId); + } + + /** + * Show a message dialog with a single "OK" button. + * + * @param context + * activity owning the dialog + * @param message + * message dialog content + */ + public static void message(final Activity context, final String message) { + message(context, null, message); + } + + /** + * Show a message dialog with a single "OK" button. + * + * @param context + * activity owning the dialog + * @param title + * message dialog title + * @param message + * message dialog content + */ + public static void message(final Activity context, final @Nullable String title, final String message) { + message(context, title, message, null); + } + + /** + * Show a message dialog with a single "OK" button and an icon. + * + * @param context + * activity owning the dialog + * @param title + * message dialog title + * @param message + * message dialog content + * @param icon + * message dialog title icon + */ + public static void message(final Activity context, final @Nullable String title, final String message, final @Nullable Drawable icon) { + Builder builder = new AlertDialog.Builder(context) + .setMessage(message) + .setCancelable(true) + .setPositiveButton(getString(android.R.string.ok), null); + if (title != null) { + builder.setTitle(title); + } + if (icon != null) { + builder.setIcon(icon); + } + builder.create().show(); + } + + /** + * Show a message dialog with a single "OK" button and an icon. + * + * @param context + * activity owning the dialog + * @param title + * message dialog title + * @param message + * message dialog content + */ + public static void message(final Activity context, final int title, final String message) { + message(context, getString(title), message); + } + + /** + * Show a message dialog with a single "OK" button and an icon. + * + * @param context + * activity owning the dialog + * @param title + * message dialog title + * @param message + * message dialog content + */ + public static void message(final Activity context, final int title, final int message) { + message(context, getString(title), getString(message)); + } + + /** + * Show a message dialog with a single "OK" button and an icon. + * + * @param context + * activity owning the dialog + * @param title + * message dialog title + * @param message + * message dialog content + * @param icon + * message dialog title icon + */ + public static void message(final Activity context, final int title, final int message, final @Nullable Drawable icon) { + message(context, getString(title), getString(message), icon); + } + +} diff --git a/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java b/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java index fc5ebe6..8660a7b 100644 --- a/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java @@ -1,5 +1,7 @@ package cgeo.geocaching.ui.dialog; +import cgeo.geocaching.utils.Log; + import android.app.Dialog; import android.content.Context; import android.os.Bundle; @@ -24,7 +26,7 @@ public abstract class NoTitleDialog extends Dialog { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } catch (final Exception e) { - // nothing + Log.e("NoTitleDialog.onCreate", e); } } } diff --git a/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java index 8fe3866..6311476 100644 --- a/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java +++ b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java @@ -1,10 +1,10 @@ package cgeo.geocaching.ui.logs; import cgeo.geocaching.CacheDetailActivity; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.ui.UserActionsClickListener; @@ -109,4 +109,4 @@ public class CacheLogsViewCreator extends LogsViewCreator { return new UserActionsClickListener(getCache()); } -}
\ No newline at end of file +} diff --git a/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java index 15634d3..fb72ee5 100644 --- a/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java +++ b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java @@ -8,7 +8,7 @@ import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; -import cgeo.geocaching.ui.AbstractCachingPageViewCreator; +import cgeo.geocaching.ui.AbstractCachingListViewPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; import cgeo.geocaching.ui.DecryptTextClickListener; import cgeo.geocaching.ui.Formatter; @@ -30,7 +30,7 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; -public abstract class LogsViewCreator extends AbstractCachingPageViewCreator<ListView> { +public abstract class LogsViewCreator extends AbstractCachingListViewPageViewCreator { protected final AbstractActivity activity; diff --git a/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java index b291a8a..4ce2e0c 100644 --- a/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java +++ b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java @@ -3,8 +3,8 @@ package cgeo.geocaching.utils; import cgeo.geocaching.DataStore; import cgeo.geocaching.MainActivity; import cgeo.geocaching.R; -import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.dialog.Dialogs; import org.apache.commons.lang3.StringUtils; @@ -42,7 +42,7 @@ public class DatabaseBackupUtils { dialog.dismiss(); boolean restored = restoreSuccessful.get(); String message = restored ? res.getString(R.string.init_restore_success) : res.getString(R.string.init_restore_failed); - ActivityMixin.helpDialog(activity, res.getString(R.string.init_backup_restore), message); + Dialogs.message(activity, R.string.init_backup_restore, message); if (activity instanceof MainActivity) { ((MainActivity) activity).updateCacheCounter(); } @@ -57,9 +57,7 @@ public class DatabaseBackupUtils { // avoid overwriting an existing backup with an empty database // (can happen directly after reinstalling the app) if (DataStore.getAllCachesCount() == 0) { - ActivityMixin.helpDialog(activity, - context.getString(R.string.init_backup), - context.getString(R.string.init_backup_unnecessary)); + Dialogs.message(activity, R.string.init_backup, R.string.init_backup_unnecessary); return false; } @@ -74,8 +72,8 @@ public class DatabaseBackupUtils { @Override public void run() { dialog.dismiss(); - ActivityMixin.helpDialog(activity, - context.getString(R.string.init_backup_backup), + Dialogs.message(activity, + R.string.init_backup_backup, backupFileName != null ? context.getString(R.string.init_backup_success) + "\n" + backupFileName diff --git a/main/src/cgeo/geocaching/utils/MatcherWrapper.java b/main/src/cgeo/geocaching/utils/MatcherWrapper.java index 78b1170..c99d3c4 100644 --- a/main/src/cgeo/geocaching/utils/MatcherWrapper.java +++ b/main/src/cgeo/geocaching/utils/MatcherWrapper.java @@ -1,5 +1,7 @@ package cgeo.geocaching.utils; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,6 +46,7 @@ public class MatcherWrapper { * @param input * @return */ + @SuppressFBWarnings("DM_STRING_CTOR") private static String newString(String input) { if (input == null) { return null; diff --git a/main/src/cgeo/geocaching/utils/TextUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java index 14caf1d..efbb2d7 100644 --- a/main/src/cgeo/geocaching/utils/TextUtils.java +++ b/main/src/cgeo/geocaching/utils/TextUtils.java @@ -3,6 +3,8 @@ */ package cgeo.geocaching.utils; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import org.eclipse.jdt.annotation.Nullable; import java.util.regex.Matcher; @@ -36,6 +38,7 @@ public final class TextUtils { * Find the last occurring value * @return defaultValue or the n-th group if the pattern matches (trimmed if wanted) */ + @SuppressFBWarnings("DM_STRING_CTOR") public static String getMatch(@Nullable final String data, final Pattern p, final boolean trim, final int group, final String defaultValue, final boolean last) { if (data != null) { diff --git a/main/templates/keys.xml b/main/templates/keys.xml new file mode 100644 index 0000000..e667df5 --- /dev/null +++ b/main/templates/keys.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources> + <!-- Google Maps --> + <string name="maps_api_key" translatable="false">@maps.api.key@</string> + + <!-- Opencaching.de --> + <string name="oc_de_okapi_consumer_key" translatable="false">@ocde.okapi.consumer.key@</string> + <string name="oc_de_okapi_consumer_secret" translatable="false">@ocde.okapi.consumer.secret@</string> + + <!-- Opencaching.pl --> + <string name="oc_pl_okapi_consumer_key" translatable="false">@ocpl.okapi.consumer.key@</string> + <string name="oc_pl_okapi_consumer_secret" translatable="false">@ocpl.okapi.consumer.secret@</string> +</resources> diff --git a/main/templates/mapsapikey.xml b/main/templates/mapsapikey.xml deleted file mode 100644 index 3655b5e..0000000 --- a/main/templates/mapsapikey.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources> - <string name="maps_api_key" translatable="false">@maps.api.key@</string> -</resources> diff --git a/main/templates/ocde_okapi.xml b/main/templates/ocde_okapi.xml deleted file mode 100644 index 9ca39b3..0000000 --- a/main/templates/ocde_okapi.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources> - <string name="oc_de_okapi_consumer_key" translatable="false">@ocde.okapi.consumer.key@</string> - <string name="oc_de_okapi_consumer_secret" translatable="false">@ocde.okapi.consumer.secret@</string> -</resources> diff --git a/main/templates/ocpl_okapi.xml b/main/templates/ocpl_okapi.xml deleted file mode 100644 index 6903ce7..0000000 --- a/main/templates/ocpl_okapi.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources> - <string name="oc_pl_okapi_consumer_key" translatable="false">@ocpl.okapi.consumer.key@</string> - <string name="oc_pl_okapi_consumer_secret" translatable="false">@ocpl.okapi.consumer.secret@</string> -</resources> diff --git a/send2cgeo/send2cgeoOcPl.user.js b/send2cgeo/send2cgeoOcPl.user.js index 0405367..77b64a0 100644 --- a/send2cgeo/send2cgeoOcPl.user.js +++ b/send2cgeo/send2cgeoOcPl.user.js @@ -1,10 +1,11 @@ // ==UserScript== -// @name Send to c:geo for opencaching.pl +// @name Send to c:geo for opencaching pl/nl // @namespace http://send2.cgeo.org/ -// @description Add button "Send to c:geo" to opencaching.pl +// @description Add button "Send to c:geo" to opencaching.pl and opencaching.nl // @include http://opencaching.pl/viewcache.php* +// @include http://www.opencaching.nl/viewcache.php* // @icon http://send2.cgeo.org/content/images/logo.png -// @version 0.1 +// @version 0.2 // ==/UserScript== // Inserts javascript that will be called by the s2cgeo button. The closure diff --git a/tests/.settings/edu.umd.cs.findbugs.core.prefs b/tests/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 0000000..a16868e --- /dev/null +++ b/tests/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,133 @@ +#FindBugs User Preferences +#Sun Dec 08 19:08:28 CET 2013 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|true +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=3 +effort=max +excludefilter0=../main/project/findbugs/exclusions.xml|true +filter_settings=Low|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|20 +filter_settings_neg=NOISE,I18N| +run_at_full_build=true diff --git a/tests/src/cgeo/geocaching/cgeoApplicationTest.java b/tests/src/cgeo/geocaching/CgeoApplicationTest.java index f218518..f772be6 100644 --- a/tests/src/cgeo/geocaching/cgeoApplicationTest.java +++ b/tests/src/cgeo/geocaching/CgeoApplicationTest.java @@ -2,8 +2,9 @@ package cgeo.geocaching; import cgeo.CGeoTestCase; import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.connector.gc.GCParser; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; @@ -40,7 +41,9 @@ import junit.framework.Assert; * application and/or context. */ -public class cgeoApplicationTest extends CGeoTestCase { +public class CgeoApplicationTest extends CGeoTestCase { + + private static final MapTokens INVALID_TOKEN = null; /** * The name 'test preconditions' is a convention to signal that if this test @@ -51,7 +54,7 @@ public class cgeoApplicationTest extends CGeoTestCase { @SuppressWarnings("static-method") @SmallTest public void testPreconditions() { - assertEquals(StatusCode.NO_ERROR, Login.login()); + assertEquals(StatusCode.NO_ERROR, GCLogin.getInstance().login()); } /** @@ -69,6 +72,7 @@ public class cgeoApplicationTest extends CGeoTestCase { @MediumTest public static void testSearchTrackable() { final Trackable tb = GCParser.searchTrackable("TB2J1VZ", null, null); + assertNotNull(tb); // fix data assertEquals("aefffb86-099f-444f-b132-605436163aa8", tb.getGuid()); assertEquals("TB2J1VZ", tb.getGeocode()); @@ -134,7 +138,7 @@ public class cgeoApplicationTest extends CGeoTestCase { * @param runnable */ private static void withMockedLoginDo(final Runnable runnable) { - final ImmutablePair<String, String> login = Settings.getGcLogin(); + final ImmutablePair<String, String> login = Settings.getGcCredentials(); final String memberStatus = Settings.getMemberStatus(); try { @@ -143,7 +147,7 @@ public class cgeoApplicationTest extends CGeoTestCase { // restore user and password TestSettings.setLogin(login.left, login.right); Settings.setMemberStatus(memberStatus); - Login.login(); + GCLogin.getInstance().login(); } } @@ -268,7 +272,7 @@ public class cgeoApplicationTest extends CGeoTestCase { public void run() { final SearchResult search = GCParser.searchByUsername("blafoo", CacheType.WEBCAM, false, null); assertNotNull(search); - assertEquals(4, search.getTotal()); + assertEquals(4, search.getTotalCountGC()); assertTrue(search.getGeocodes().contains("GCP0A9")); } }); @@ -295,7 +299,7 @@ public class cgeoApplicationTest extends CGeoTestCase { final GC2CJPF mockedCache = new GC2CJPF(); deleteCacheFromDB(mockedCache.getGeocode()); - final String[] tokens = Login.getMapTokens(); + final MapTokens tokens = GCLogin.getMapTokens(); final Viewport viewport = new Viewport(mockedCache, 0.003, 0.003); // check coords for DETAILED @@ -344,8 +348,6 @@ public class cgeoApplicationTest extends CGeoTestCase { try { - final String[] tokens = null; // without a valid token we are "logged off" - // non premium cache MockedCache cache = new GC2CJPF(); deleteCacheFromDBAndLogout(cache.getGeocode()); @@ -353,7 +355,7 @@ public class cgeoApplicationTest extends CGeoTestCase { Settings.setCacheType(CacheType.ALL); Viewport viewport = new Viewport(cache, 0.003, 0.003); - SearchResult search = ConnectorFactory.searchByViewport(viewport, tokens); + SearchResult search = ConnectorFactory.searchByViewport(viewport, INVALID_TOKEN); assertNotNull(search); assertTrue(search.getGeocodes().contains(cache.getGeocode())); @@ -370,7 +372,7 @@ public class cgeoApplicationTest extends CGeoTestCase { deleteCacheFromDBAndLogout(cache.getGeocode()); viewport = new Viewport(cache, 0.003, 0.003); - search = ConnectorFactory.searchByViewport(viewport, tokens); + search = ConnectorFactory.searchByViewport(viewport, INVALID_TOKEN); assertNotNull(search); // depending on the chosen strategy the cache is part of the search or not @@ -392,7 +394,7 @@ public class cgeoApplicationTest extends CGeoTestCase { String oldUser = mockedCache.getMockedDataUser(); try { mockedCache.setMockedDataUser(Settings.getUsername()); - Geocache parsedCache = cgeoApplicationTest.testSearchByGeocode(mockedCache.getGeocode()); + Geocache parsedCache = CgeoApplicationTest.testSearchByGeocode(mockedCache.getGeocode()); if (null != parsedCache) { Compare.assertCompareCaches(mockedCache, parsedCache, true); } @@ -406,10 +408,10 @@ public class cgeoApplicationTest extends CGeoTestCase { * Caches that are good test cases */ public static void testSearchByGeocodeSpecialties() { - final Geocache GCV2R9 = cgeoApplicationTest.testSearchByGeocode("GCV2R9"); + final Geocache GCV2R9 = CgeoApplicationTest.testSearchByGeocode("GCV2R9"); Assert.assertEquals("California, United States", GCV2R9.getLocation()); - final Geocache GC1ZXEZ = cgeoApplicationTest.testSearchByGeocode("GC1ZXEZ"); + final Geocache GC1ZXEZ = CgeoApplicationTest.testSearchByGeocode("GC1ZXEZ"); Assert.assertEquals("Ms.Marple/Mr.Stringer", GC1ZXEZ.getOwnerUserId()); } @@ -417,7 +419,7 @@ public class cgeoApplicationTest extends CGeoTestCase { private static void deleteCacheFromDBAndLogout(String geocode) { deleteCacheFromDB(geocode); - Login.logout(); + GCLogin.getInstance().logout(); // Modify login data to avoid an automatic login again TestSettings.setLogin("c:geo", "c:geo"); Settings.setMemberStatus("Basic member"); diff --git a/tests/src/cgeo/geocaching/GeocacheTest.java b/tests/src/cgeo/geocaching/GeocacheTest.java index 5d9a31c..cdca9b7 100644 --- a/tests/src/cgeo/geocaching/GeocacheTest.java +++ b/tests/src/cgeo/geocaching/GeocacheTest.java @@ -245,6 +245,8 @@ public class GeocacheTest extends CGeoTestCase { assertTime("text 14:20.", 14, 20); assertTime("<b>14:20</b>", 14, 20); assertTime("<u><em>Uhrzeit:</em></u> 17-20 " + timeHours + "</span></strong>", 17, 00); + assertTime("von 11 bis 13 " + timeHours, 11, 00); + assertTime("from 11 to 13 " + timeHours, 11, 00); } private static void assertTime(final String description, final int hours, final int minutes) { diff --git a/tests/src/cgeo/geocaching/HtmlPerformanceTest.java b/tests/src/cgeo/geocaching/HtmlPerformanceTest.java index b08b06e..695cd4a 100644 --- a/tests/src/cgeo/geocaching/HtmlPerformanceTest.java +++ b/tests/src/cgeo/geocaching/HtmlPerformanceTest.java @@ -2,6 +2,8 @@ package cgeo.geocaching; import cgeo.geocaching.utils.Log; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + import org.apache.commons.lang3.StringEscapeUtils; import android.os.SystemClock; @@ -51,6 +53,7 @@ public class HtmlPerformanceTest extends AndroidTestCase { }); } + @SuppressFBWarnings("DM_GC") private static long measure(String label, Runnable runnable) { System.gc(); final long start = SystemClock.elapsedRealtime(); diff --git a/tests/src/cgeo/geocaching/PersonalNoteTest.java b/tests/src/cgeo/geocaching/PersonalNoteTest.java index 38085d5..74e1bab 100644 --- a/tests/src/cgeo/geocaching/PersonalNoteTest.java +++ b/tests/src/cgeo/geocaching/PersonalNoteTest.java @@ -36,10 +36,10 @@ public class PersonalNoteTest extends TestCase { Geocache exceedingCache = new Geocache(); exceedingCache.setListId(StoredList.STANDARD_LIST_ID); // stored - exceedingCache.setPersonalNote(testString.toString()); + exceedingCache.setPersonalNote(testString); PersonalNote otherNote = new PersonalNote(exceedingCache); PersonalNote result = parsedNote.mergeWith(otherNote); - assertPersonalNote(result, null, testString.toString()); + assertPersonalNote(result, null, testString); } public static void testParseCgeoOnly() { diff --git a/tests/src/cgeo/geocaching/SearchResultTest.java b/tests/src/cgeo/geocaching/SearchResultTest.java index 9bce5a5..3f9f07f 100644 --- a/tests/src/cgeo/geocaching/SearchResultTest.java +++ b/tests/src/cgeo/geocaching/SearchResultTest.java @@ -13,7 +13,7 @@ public class SearchResultTest extends AndroidTestCase { geocodes.add("GC23456"); final SearchResult searchResult = new SearchResult(geocodes); assertEquals(2, searchResult.getCount()); - assertEquals(2, searchResult.getTotal()); + assertEquals(2, searchResult.getTotalCountGC()); assertTrue(searchResult.getGeocodes().contains("GC12345")); } diff --git a/tests/src/cgeo/geocaching/connector/ConnectorFactoryTest.java b/tests/src/cgeo/geocaching/connector/ConnectorFactoryTest.java index ca396e1..e41e316 100644 --- a/tests/src/cgeo/geocaching/connector/ConnectorFactoryTest.java +++ b/tests/src/cgeo/geocaching/connector/ConnectorFactoryTest.java @@ -5,12 +5,14 @@ import cgeo.geocaching.connector.oc.OCConnector; import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase; import cgeo.geocaching.test.mock.GC1ZXX2; +import java.util.Collection; + public class ConnectorFactoryTest extends AbstractResourceInstrumentationTestCase { public static void testGetConnectors() { - final IConnector[] connectors = ConnectorFactory.getConnectors(); + final Collection<IConnector> connectors = ConnectorFactory.getConnectors(); assertNotNull(connectors); - assertTrue(connectors.length > 0); // unknown connector must exist + assertFalse(connectors.isEmpty()); // unknown connector must exist } public static void testCanHandle() { diff --git a/tests/src/cgeo/geocaching/connector/ec/ECConnectorTest.java b/tests/src/cgeo/geocaching/connector/ec/ECConnectorTest.java new file mode 100644 index 0000000..4b9ae37 --- /dev/null +++ b/tests/src/cgeo/geocaching/connector/ec/ECConnectorTest.java @@ -0,0 +1,33 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LogType; + +import java.util.List; + +import junit.framework.TestCase; + +public class ECConnectorTest extends TestCase { + + public static void testCanHandle() throws Exception { + assertTrue(ECConnector.getInstance().canHandle("EC380")); + assertFalse(ECConnector.getInstance().canHandle("GC380")); + assertFalse("faked EC codes must be handled during the import, otherwise GCECxxxx codes belong to 2 connectors", ECConnector.getInstance().canHandle("GCEC380")); + } + + public static void testGetPossibleLogTypes() throws Exception { + final List<LogType> possibleLogTypes = ECConnector.getInstance().getPossibleLogTypes(createCache()); + assertNotNull(possibleLogTypes); + assertFalse(possibleLogTypes.isEmpty()); + assertTrue(possibleLogTypes.contains(LogType.FOUND_IT)); + } + + private static Geocache createCache() { + final Geocache geocache = new Geocache(); + geocache.setType(CacheType.TRADITIONAL); + geocache.setGeocode("EC727"); + return geocache; + } + +} diff --git a/tests/src/cgeo/geocaching/connector/gc/GCConnectorTest.java b/tests/src/cgeo/geocaching/connector/gc/GCConnectorTest.java index d3e7a27..0caa7d4 100644 --- a/tests/src/cgeo/geocaching/connector/gc/GCConnectorTest.java +++ b/tests/src/cgeo/geocaching/connector/gc/GCConnectorTest.java @@ -20,9 +20,9 @@ public class GCConnectorTest extends AbstractResourceInstrumentationTestCase { // set up settings required for test TestSettings.setExcludeMine(false); Settings.setCacheType(CacheType.ALL); - Login.login(); + GCLogin.getInstance().login(); - final String[] tokens = Login.getMapTokens(); + final MapTokens tokens = GCLogin.getMapTokens(); { final Viewport viewport = new Viewport(new Geopoint("N 52° 25.369 E 9° 35.499"), new Geopoint("N 52° 25.600 E 9° 36.200")); diff --git a/tests/src/cgeo/geocaching/connector/gc/GCConstantsTest.java b/tests/src/cgeo/geocaching/connector/gc/GCConstantsTest.java index 40062f1..60ff500 100644 --- a/tests/src/cgeo/geocaching/connector/gc/GCConstantsTest.java +++ b/tests/src/cgeo/geocaching/connector/gc/GCConstantsTest.java @@ -45,10 +45,10 @@ public class GCConstantsTest extends AndroidTestCase { */ public static void testCacheCountOnline() { - Login.logout(); - Login.setActualCachesFound(0); - Login.login(); - assertTrue(Login.getActualCachesFound() > 0); + GCLogin.getInstance().logout(); + GCLogin.getInstance().setActualCachesFound(0); + GCLogin.getInstance().login(); + assertTrue(GCLogin.getInstance().getActualCachesFound() > 0); } public static void testConstants() { diff --git a/tests/src/cgeo/geocaching/enumerations/CacheSizeTest.java b/tests/src/cgeo/geocaching/enumerations/CacheSizeTest.java index 9c3063d..2f11dfc 100644 --- a/tests/src/cgeo/geocaching/enumerations/CacheSizeTest.java +++ b/tests/src/cgeo/geocaching/enumerations/CacheSizeTest.java @@ -27,4 +27,11 @@ public class CacheSizeTest extends AndroidTestCase { assertEquals(size, CacheSize.getById(size.id.toUpperCase(Locale.US))); } } + + public static void testGetByIdNumeric() { + assertEquals(CacheSize.REGULAR, CacheSize.getById("3")); + assertEquals(CacheSize.UNKNOWN, CacheSize.getById("0")); + assertEquals(CacheSize.UNKNOWN, CacheSize.getById("9")); + assertEquals(CacheSize.UNKNOWN, CacheSize.getById("-1")); + } } diff --git a/tests/src/cgeo/geocaching/export/ExportTest.java b/tests/src/cgeo/geocaching/export/ExportTest.java index 6d39f8d..b8f1ffd 100644 --- a/tests/src/cgeo/geocaching/export/ExportTest.java +++ b/tests/src/cgeo/geocaching/export/ExportTest.java @@ -7,6 +7,7 @@ import cgeo.geocaching.LogEntry; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.utils.FileUtils; import java.io.File; import java.util.ArrayList; @@ -43,7 +44,7 @@ public class ExportTest extends CGeoTestCase { assertNotNull(result); - result.delete(); + FileUtils.deleteIgnoringFailure(result); } private static class GpxExportTester extends GpxExport { diff --git a/tests/src/cgeo/geocaching/files/GPXImporterTest.java b/tests/src/cgeo/geocaching/files/GPXImporterTest.java index ee42d23..6d7456c 100644 --- a/tests/src/cgeo/geocaching/files/GPXImporterTest.java +++ b/tests/src/cgeo/geocaching/files/GPXImporterTest.java @@ -12,6 +12,7 @@ import cgeo.geocaching.test.R; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import android.net.Uri; @@ -50,8 +51,8 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { // the "real" method check assertEquals(wptsFileName, GPXImporter.getWaypointsFileNameForGpxFile(gpx)); // they also need to be deleted, because of case sensitive tests that will not work correct on case insensitive file systems - gpx.delete(); - wpts.delete(); + FileUtils.deleteQuietly(gpx); + FileUtils.deleteQuietly(wpts); } final File gpx1 = new File(tempDir, "abc.gpx"); assertNull(GPXImporter.getWaypointsFileNameForGpxFile(gpx1)); @@ -260,7 +261,7 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { msg1.copyFrom(msg); messages.add(msg1); lastMessage = System.currentTimeMillis(); - notify(); + notifyAll(); } public synchronized void waitForCompletion(final long milliseconds, final int maxMessages) { @@ -287,7 +288,7 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { assertTrue("java.io.tmpdir is not defined", StringUtils.isNotBlank(globalTempDir)); tempDir = new File(globalTempDir, "cgeogpxesTest"); - tempDir.mkdir(); + cgeo.geocaching.utils.FileUtils.mkdirs(tempDir); assertTrue("Could not create directory " + tempDir.getPath(), tempDir.exists()); // workaround to get storage initialized DataStore.getAllHistoryCachesCount(); @@ -306,21 +307,9 @@ public class GPXImporterTest extends AbstractResourceInstrumentationTestCase { cachesInList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); DataStore.markDropped(cachesInList); DataStore.removeList(listId); - deleteDirectory(tempDir); + FileUtils.deleteDirectory(tempDir); TestSettings.setStoreOfflineMaps(importCacheStaticMaps); TestSettings.setStoreOfflineWpMaps(importWpStaticMaps); super.tearDown(); } - - private static void deleteDirectory(File dir) { - for (File f : dir.listFiles()) { - if (f.isFile()) { - f.delete(); - } else if (f.isDirectory()) { - deleteDirectory(f); - } - } - dir.delete(); - } - } diff --git a/tests/src/cgeo/geocaching/files/GPXParserTest.java b/tests/src/cgeo/geocaching/files/GPXParserTest.java index 9604519..dcaad22 100644 --- a/tests/src/cgeo/geocaching/files/GPXParserTest.java +++ b/tests/src/cgeo/geocaching/files/GPXParserTest.java @@ -12,13 +12,13 @@ import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase; import cgeo.geocaching.test.R; +import cgeo.geocaching.utils.SynchronizedDateFormat; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; @@ -28,7 +28,7 @@ import java.util.Locale; import java.util.Set; public class GPXParserTest extends AbstractResourceInstrumentationTestCase { - private static final SimpleDateFormat LOG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); // 2010-04-20T07:00:00Z + private static final SynchronizedDateFormat LOG_DATE_FORMAT = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); // 2010-04-20T07:00:00Z public void testGPXVersion100() throws Exception { testGPXVersion(R.raw.gc1bkp3_gpx100); diff --git a/tests/src/cgeo/geocaching/filter/TerrainFilterTest.java b/tests/src/cgeo/geocaching/filter/TerrainFilterTest.java index 234af7e..78bd14d 100644 --- a/tests/src/cgeo/geocaching/filter/TerrainFilterTest.java +++ b/tests/src/cgeo/geocaching/filter/TerrainFilterTest.java @@ -19,6 +19,6 @@ public class TerrainFilterTest extends CGeoTestCase { } public static void testAllFilters() { - assertTrue(new TerrainFilter.Factory().getFilters().size() == 5); // terrain ranges from 1 to 5 + assertTrue(new TerrainFilter.Factory().getFilters().size() == 7); // terrain ranges from 1 to 7 (due to ExtremCaching.com using that value) } } diff --git a/tests/src/cgeo/geocaching/settings/TestSettings.java b/tests/src/cgeo/geocaching/settings/TestSettings.java index d63200b..2e6809f 100644 --- a/tests/src/cgeo/geocaching/settings/TestSettings.java +++ b/tests/src/cgeo/geocaching/settings/TestSettings.java @@ -24,8 +24,8 @@ public final class TestSettings extends Settings { putBoolean(R.string.pref_excludemine, exclude); } - public static boolean setLogin(final String username, final String password) { - return Settings.setLogin(username, password); + public static void setLogin(final String username, final String password) { + Settings.setLogin(username, password); } public static void setStoreOfflineMaps(final boolean offlineMaps) { diff --git a/tests/src/cgeo/geocaching/test/CgeoTestsActivity.java b/tests/src/cgeo/geocaching/test/CgeoTestsActivity.java index 0f41cef..c1f6fdd 100644 --- a/tests/src/cgeo/geocaching/test/CgeoTestsActivity.java +++ b/tests/src/cgeo/geocaching/test/CgeoTestsActivity.java @@ -1,5 +1,7 @@ package cgeo.geocaching.test; +import butterknife.ButterKnife; + import android.app.Activity; import android.content.ComponentName; import android.content.pm.InstrumentationInfo; @@ -14,6 +16,7 @@ import android.widget.TextView; import android.widget.Toast; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.util.List; @@ -76,7 +79,9 @@ public class CgeoTestsActivity extends Activity { Thread.sleep(50); publishProgress(mReader.readLine()); } while (System.currentTimeMillis() < timeout); - } catch (Exception e) { + } catch (InterruptedException e) { + publishProgress("ERROR: " + e); + } catch (IOException e) { publishProgress("ERROR: " + e); } finally { publishProgress("END"); @@ -90,8 +95,8 @@ public class CgeoTestsActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.cgeo_tests_activity); - logView = (TextView) findViewById(R.id.logOutput); - scrollView = (BottomAwareScrollView) findViewById(R.id.scrollView); + logView = ButterKnife.findById(this, R.id.logOutput); + scrollView = ButterKnife.findById(this, R.id.scrollView); } @Override @@ -114,7 +119,7 @@ public class CgeoTestsActivity extends Activity { * referenced from XML layout */ public void runTests(final View v) { - final Button button = (Button) findViewById(R.id.buttonRun); + final Button button = ButterKnife.findById(this, R.id.buttonRun); button.setEnabled(false); try { runTestsInternally(); diff --git a/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java b/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java index 1e4b527..09139f2 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java +++ b/tests/src/cgeo/geocaching/test/mock/GC1ZXX2.java @@ -1,6 +1,6 @@ package cgeo.geocaching.test.mock; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; @@ -86,7 +86,7 @@ public class GC1ZXX2 extends MockedCache { @Override public Date getHiddenDate() { try { - return Login.parseGcCustomDate("16/10/2009", getDateFormat()); + return GCLogin.parseGcCustomDate("16/10/2009", getDateFormat()); } catch (ParseException e) { // intentionally left blank } diff --git a/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java b/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java index 9ca8552..c125422 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java +++ b/tests/src/cgeo/geocaching/test/mock/GC2CJPF.java @@ -1,6 +1,6 @@ package cgeo.geocaching.test.mock; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; @@ -121,7 +121,7 @@ public class GC2CJPF extends MockedCache { @Override public Date getHiddenDate() { try { - return Login.parseGcCustomDate("31/07/2010", getDateFormat()); + return GCLogin.parseGcCustomDate("31/07/2010", getDateFormat()); } catch (ParseException e) { // intentionally left blank } diff --git a/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java b/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java index e85c3b9..a1337cd 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java +++ b/tests/src/cgeo/geocaching/test/mock/GC2JVEH.java @@ -2,7 +2,7 @@ package cgeo.geocaching.test.mock; import cgeo.geocaching.Image; import cgeo.geocaching.Trackable; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; @@ -89,7 +89,7 @@ public class GC2JVEH extends MockedCache { @Override public Date getHiddenDate() { try { - return Login.parseGcCustomDate("28/11/2010", getDateFormat()); + return GCLogin.parseGcCustomDate("28/11/2010", getDateFormat()); } catch (ParseException e) { // intentionally left blank } diff --git a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java index ae3c87d..ea8079b 100644 --- a/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java +++ b/tests/src/cgeo/geocaching/test/mock/GC3XX5J.java @@ -1,6 +1,6 @@ package cgeo.geocaching.test.mock; -import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; @@ -86,7 +86,7 @@ public class GC3XX5J extends MockedCache { @Override public Date getHiddenDate() { try { - return Login.parseGcCustomDate("2012-10-01", getDateFormat()); + return GCLogin.parseGcCustomDate("2012-10-01", getDateFormat()); } catch (ParseException e) { // intentionally left blank } @@ -133,10 +133,4 @@ public class GC3XX5J extends MockedCache { public String getShortDescription() { return "Kadar zbolimo nam pomaga...<br /> <br /> When we get sick, they are helpful..."; } - - @Override - public String getPersonalNote() { - return super.getPersonalNote(); - } - } |
