From db6d11539edd5b962670a1a9ed898f7a925326fd Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 27 Sep 2010 11:21:22 -0700 Subject: [PATCH 01/12] Astrid unit tests: use zutubi junit xml reporter to generate junit xml. also use emma xml reporter to generate emma xml --- tests/.classpath | 1 + tests/.gitignore | 1 + tests/AndroidManifest.xml | 2 +- tests/build.properties | 5 ++ tests/build.xml | 65 ++++++++++++++++++++-- tests/libs/android-junit-report-1.0.3.jar | Bin 0 -> 5608 bytes 6 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 tests/libs/android-junit-report-1.0.3.jar diff --git a/tests/.classpath b/tests/.classpath index 1f0940313..33c1872af 100644 --- a/tests/.classpath +++ b/tests/.classpath @@ -4,5 +4,6 @@ + diff --git a/tests/.gitignore b/tests/.gitignore index 6a4fa7410..6f60917b0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -2,3 +2,4 @@ bin/ instrumented/ coverage.em coverage/ +reports/ \ No newline at end of file diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 78e3d25bd..af07d58d8 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -19,7 +19,7 @@ the package of the parent app. To run the tests use the command: "adb shell am instrument -w com.xxx.xxx.tests/android.test.InstrumentationTestRunner" --> - diff --git a/tests/build.properties b/tests/build.properties index bfacee704..dd82656f6 100644 --- a/tests/build.properties +++ b/tests/build.properties @@ -24,3 +24,8 @@ out.dir=bin # because we pull in from multiple source folders source.dir=${out.dir}/source +# test runner: junit report test runner +test.runner=com.zutubi.android.junitreport.JUnitReportTestRunner + +# reports output directory +reports.dir=reports \ No newline at end of file diff --git a/tests/build.xml b/tests/build.xml index af5e730b2..b30dfbcff 100644 --- a/tests/build.xml +++ b/tests/build.xml @@ -64,7 +64,14 @@ --> - + + + + + + + + @@ -77,9 +84,59 @@ - - - + + + + + + + + + + + + + + + + + + + Downloading coverage file into project directory... + + + + + + + Extracting coverage report... + + + + + + + + + + + + + + Cleaning up temporary files... + + + + Saving the report file in ${basedir}/coverage/coverage.html + + + diff --git a/tests/libs/android-junit-report-1.0.3.jar b/tests/libs/android-junit-report-1.0.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4146976f7e6b9741d368c88b10f7c144569af05 GIT binary patch literal 5608 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 Date: Mon, 27 Sep 2010 15:35:29 -0700 Subject: [PATCH 02/12] emma html encoding -> utf-8 --- tests/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/build.xml b/tests/build.xml index b30dfbcff..4c26aec43 100644 --- a/tests/build.xml +++ b/tests/build.xml @@ -127,7 +127,7 @@ - + From 1bf0e9e494f0a7624e6937a28253b5a4787dcb68 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 27 Sep 2010 15:52:54 -0700 Subject: [PATCH 03/12] Custom modified JUnitReportTestRunner to report test times --- tests/libs/android-junit-report-1.0.3.jar | Bin 5608 -> 0 bytes .../junitreport/JUnitReportListener.java | 250 ++++++++++++++++++ .../junitreport/JUnitReportTestRunner.java | 98 +++++++ 3 files changed, 348 insertions(+) delete mode 100644 tests/libs/android-junit-report-1.0.3.jar create mode 100644 tests/src/com/zutubi/android/junitreport/JUnitReportListener.java create mode 100644 tests/src/com/zutubi/android/junitreport/JUnitReportTestRunner.java diff --git a/tests/libs/android-junit-report-1.0.3.jar b/tests/libs/android-junit-report-1.0.3.jar deleted file mode 100644 index a4146976f7e6b9741d368c88b10f7c144569af05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5608 zcmbW5byyr(*2W1E+#xuG5F8pE+}#@Y;ND2%?wa5b2p$}QOK=a3LvRbh8V|u80)Zuy z$z*4CX7=0f^i$RMkMq9gxuY~iDic&1M zgD^14zojCE|bv8Q0|!hbU7U;I@_|=(jt}MYXaSk5z>~X#$!m%&1ES_J3ADU z(Ph6ViCg`o!jZOHm4BnTp<}pf4xJo_J0`p?>Uyev0|)47$-uMZB~9_WQ^Wxtrj*knC`qCHaZn30<9 zylVLY2U}yQ9P%QDCn+kd-VXeD(j6yoBJ;HA^N!5<*Wttvt7@|C z?heP;efPKC(!Jr?o1d{j36vBC$l@Sv?%+0H<5P9CjZRHUtsOsZw!Jw6(^z({aqje+ z&}?Aij0XG0O1}vm9{wDIxMCV=`MOJfNj*Awu>XVJeM^fNyVKl zP&Tae?f@BC=y0{(5&F30_+Y9Q8xt10Z(1tP*rzPIHkUJl$|hKbT91LMu+36;oI(zeKAHjTYBU}4QtIn+VOD(yvYROtl5y4JveRUAqQ*ue>o zEkXS7UkzCHySGB8SUip-7dCsG77CzXQNLKDo0kL^CRqd*D}D%=NT+@*VU8lZ7z~6e z4&iYp*+Jqc6`F@}QP0~BO&g-5TalnDM6sUZA<}Igq)IOmXc2bQOgukEc6msV3NWq> z88CH%s|DUbJrP)5>?N=ps2{!zs-*MK>D0pa0d_K?7-$^2A}lK(0uh!~5ABGS zC9xJy_nR>@o0iY2^6HBLpJ~}#l*Cj_`wCW{x0~a%uJ1*jyL5ld#VI)sfS25T_AJ*9 z(9Ddp9V1ip==wY<#z85B_7~=A?R-;nrLywM(t?B+0|*i}jnu?s;y63{(l47~b8DE1 z?R0<(wHkJN%z`W0hD;L^A7^Hluw&{Z`vo>`R2At#Ugq2UOvp75T^eKJ0SP*QG7;lC zdAzL?UoIwbVGq{l9Znn6ym&VV5l3AkQf93zwAp#aXe8G~UtT_C&q{%qvQx`PDt>yK zE+MDjCBG!Y=dLhbtdH@E=$op=V`IfR(c9`PdJ@8mX&gXZ{9!gv90skY9I`v4$dq>> zZWP;xg<)qn0_9RcI5f`_h}syFmlSAghw1h67hl*EpAk>vxW$v($iKX0M(&Gtv9cT) zv{MX|!UqqJKb(wFu(HFvP)vjjFzr*_9pQ|851vvss9wHNBFbrxgnkr^DNhH;P!H*m+s7wf4iFm+|ACYgNRPAjmKON!a~ zM3OJhfK&9pxN$GhCUOmE(!4JjL}> z86xdr4V_~(b78f<8DvH^hd*jkPF0!1yuoHbeI%b~OOei(v_AB!J&H3z={})lDgV`_ z3|ZTbLaZg5$-ukJ>IM`z2S1QJ#M?o)kKcS4mN^{tx*T1mk$g~U-$Gh zj>M@*iphMX^y?0C!xHY@A$6g)&isH=Uvi05@$v{4U*|@>XY|mSs6+Wk{wBgJ#JZ(W zE}g6oF-}g56XZHVe?>n`OaLK~LJ&Uvr??1P6Cwv}3;4OVf(m#9M@z z63$3AQ_2bMmA~;(_|U>_Uto*uEPtjEXu3bXukDuZ3>O|>^Z66i`yNixor#JGCROlA z_-VN?ApY(-j;BaNhPP!&$9iok89Hc0u29T4`1ZAv&#fyfT_U&xYfjO$dtT;0!yFst%h{*P8RxLX|9*Lv|$VXbN;=btx@WE8**VUPyaPSe{^2Z}V_bwHHV0Xe7JP zsQ)gWg{#d}n3TW4GNC^8lfIqeiU55OA-%q-c)$4S-ooN2|7b@sPZ}W9~_H>G*ZmW?ZbY7ju!Zhk(&kAo9t4w>3svEAnGzc z3#QW4iy%`D+Cj2D+(>|bwU$$DoC+lOB_D0d)G$x{UHi--&Rzg!4Ym~Rh5k*Sb{Ks~ zOC;0_vJ(Nl3RzZxAALefA5@Ag9SKc9>WhGehImInbs;;EP}vY}yv{dROWN>R3MrUK z-lJQ=UpG)8x5wDV{+?v>hC=we_7EX0dU0vt*#~0utAaO*=%gY#*^a`4aKP&o5z~;=+lm4;*E{!piS8h`)VqO*|50<&)jpN-Ai=S&F&J$fn z9xqL=D5$0-;uMB3(T6ILoRHf~#OSpi_Y6gq9_|BXTV&Q)3o%Wl-c;SNT6aIE8EqvW zB2#$1(_e=D9>BwA6E$B4tQb`Q0x20!avXeFW+QdbldIBLB$1L=Y=sITjTvW6L+8es zrm{;*FZR%%J+j#@=pR!w5$t{;b9A=-#evKbGI?Uvn_)Rd4sFN~R=Q%{-0#bHCt9UY z5)vN-(Gm`7c-2+kwB(F+BIS^a>g9Vv_8g^mR(#V7VV03CIQG4lUZ`XvG2@|1nrpP( zrHi!h(&5R12*LYTpKg1=qU8nY5EW`Df4F2-cJ`3X&>T>I_7Q@_z>tPslZiWWuy*g) z>%&{B4FD6hJLZOgKdBQ7m20ZU7M~M!XegboWioDVFB`q^T*Vq&fuCH0njFS4)=eAY z*w}RUWQ1T5Z-~f2E}Yduo z1o-rmo0)JE?_qBBK?=aN1bnzT6|9JQd9k}kpp;Z@FD{0rrQyT{@f%-(rsNt=3h$d+ zp9l+l*CR-)4(g(r>}vD@@yWwtglSFV9^+c_a`P>MZ0wA|jz%2s;PWt$eV$FFRYF%0 zL6&g_4c%r`-Un%4_N1Ita#zQ8!?+7u>H{RGXr@s2)ivwpDx2b)K1aUH$6qJcJzfr< z;Mvz&4qTDiCvOeh;ZV1}BUzF3x^ohOWn4g#5g=L|m*;+DzABtW5{O0_A44-zJ;{%0 zj2yX-=3ar7YMs9(+0*c`k3FB!{G!`9*#1fn-;i;ZVVR-Az>s9t+R*#dPv*LpUQb+02+V(u zJVLobtoGihGW5d#k2_vWUII@r zou`_4ipP;W@u8S%_GP@3@<&w}dY+1eX8dT!MAp4`ggs073~#&pam+3?T*7)eT*6|_ z!zk|e7wa_O1olMCi6!rgg}x#h# z4e7e0IoqeQSLBEdK?{{uW7UOm8s_cAQ)#djZ>r;anFCYhob#s3e&I^jv}ps+=tf6K zIxqQH-Pw{_>w9lg2u`EtEX&9!BB`^FJQ2qL5pH^Ebsu}jlh6S1 zTmxN9jP^bzQ6ShA7g9RxPsNncp=^5)MDi_l2ECIwguqW0?KDR7^DXse1hjK=0?BCF zAEiXi`>`E8!8G~$@GW=FL)CF7vs3w@9`8@7_jK7pqiP54_G zsevP}904SR&F6G8no=yB57W_X@C=$fbx|g0E!|ghNSD7-Yv$=%>>{{n-L4`E6L$rq zxR~0_9>_8sji;^5bB`FmH^Eq^ufvW~73R+`{n`+AiY6Sxk~C22L4Dm=_=1w?$}rb0 z+O1QA@r+WgxR1~i&vcZLMCc9zH#4!5z#dBND1X3ah~N-Dz}5H8$bo(~)G zK2t0_B_HLvzH-qjuGPgCk1i&`K)*_t7aR=y9q~dm)?=6Ys)Ie@Omc9C6#8+@e@A#>WbPih5M8$!pi269JaqhAD-uHfES z_t8JX-O5WCUZpzM>z<%4)I7O*8sl_Dq%8jc77y<4$=Lnvejivcd!pa|e}`Y+IoS{C zUz4(Lf^U%@$asJLUHWg_?4OE%15@AQTk+fb;de~+qvn62tZ$&|dwf$oe(+uKJGT0J z;Wu#gQz-jK;ZKb9_XgiU)=vYkzc%eL-W3Hd={%ex)v#P*- z_rJCKx90um!M{)8FRFfj#{ap>pWhxos{k + *
  • Multiple suites are all placed in a single file under a root + * <testsuites> element.
  • + *
  • Redundant information about the number of nested cases within a suite is + * omitted.
  • + *
  • Neither standard output nor system properties are included.
  • + * + * The differences mainly revolve around making this reporting as lightweight as + * possible. The report is streamed as the tests run, making it impossible to, + * e.g. include the case count in a <testsuite> element. + */ +public class JUnitReportListener implements TestListener { + private static final String LOG_TAG = "JUnitReportListener"; + + private static final String ENCODING_UTF_8 = "utf-8"; + + private static final String TAG_SUITES = "testsuites"; + private static final String TAG_SUITE = "testsuite"; + private static final String TAG_CASE = "testcase"; + private static final String TAG_ERROR = "error"; + private static final String TAG_FAILURE = "failure"; + + private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_CLASS = "classname"; + private static final String ATTRIBUTE_TYPE = "type"; + private static final String ATTRIBUTE_MESSAGE = "message"; + private static final String ATTRIBUTE_TIME = "time"; + + // With thanks to org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner. + // Trimmed some entries, added others for Android. + private static final String[] DEFAULT_TRACE_FILTERS = new String[] { + "junit.framework.TestCase", "junit.framework.TestResult", + "junit.framework.TestSuite", + "junit.framework.Assert.", // don't filter AssertionFailure + "java.lang.reflect.Method.invoke(", "sun.reflect.", + // JUnit 4 support: + "org.junit.", "junit.framework.JUnit4TestAdapter", " more", + // Added for Android + "android.test.", "android.app.Instrumentation", + "java.lang.reflect.Method.invokeNative", + }; + + private Context mContext; + private String mReportFilePath; + private boolean mFilterTraces; + private FileOutputStream mOutputStream; + private XmlSerializer mSerializer; + private String mCurrentSuite; + + // simple time tracking + private boolean timeAlreadyWritten = false; + private long testStart; + + /** + * Creates a new listener. + * + * @param context context of the target application under test + * @param reportFilePath path of the report file to create (under the + * context using {@link Context#openFileOutput(String, int)}). + * @param filterTraces if true, stack traces will have common noise (e.g. + * framework methods) omitted for clarity + */ + public JUnitReportListener(Context context, String reportFilePath, boolean filterTraces) { + this.mContext = context; + this.mReportFilePath = reportFilePath; + this.mFilterTraces = filterTraces; + } + + @Override + public void startTest(Test test) { + try { + openIfRequired(test); + + if (test instanceof TestCase) { + TestCase testCase = (TestCase) test; + checkForNewSuite(testCase); + testStart = System.currentTimeMillis(); + timeAlreadyWritten = false; + mSerializer.startTag("", TAG_CASE); + mSerializer.attribute("", ATTRIBUTE_CLASS, mCurrentSuite); + mSerializer.attribute("", ATTRIBUTE_NAME, testCase.getName()); + } + } catch (IOException e) { + Log.e(LOG_TAG, safeMessage(e)); + } + } + + private void checkForNewSuite(TestCase testCase) throws IOException { + String suiteName = testCase.getClass().getName(); + if (mCurrentSuite == null || !mCurrentSuite.equals(suiteName)) { + if (mCurrentSuite != null) { + mSerializer.endTag("", TAG_SUITE); + } + + mSerializer.startTag("", TAG_SUITE); + mSerializer.attribute("", ATTRIBUTE_NAME, suiteName); + mCurrentSuite = suiteName; + } + } + + private void openIfRequired(Test test) throws IOException { + if (mOutputStream == null) { + mOutputStream = mContext.openFileOutput(mReportFilePath, 0); + mSerializer = Xml.newSerializer(); + mSerializer.setOutput(mOutputStream, ENCODING_UTF_8); + mSerializer.startDocument(ENCODING_UTF_8, true); + mSerializer.startTag("", TAG_SUITES); + } + } + + @Override + public void addError(Test test, Throwable error) { + addProblem(TAG_ERROR, error); + } + + @Override + public void addFailure(Test test, AssertionFailedError error) { + addProblem(TAG_FAILURE, error); + } + + private void addProblem(String tag, Throwable error) { + try { + recordTestTime(); + + mSerializer.startTag("", tag); + mSerializer.attribute("", ATTRIBUTE_MESSAGE, safeMessage(error)); + mSerializer.attribute("", ATTRIBUTE_TYPE, error.getClass().getName()); + StringWriter w = new StringWriter(); + error.printStackTrace(mFilterTraces ? new FilteringWriter(w) : new PrintWriter(w)); + mSerializer.text(w.toString()); + mSerializer.endTag("", tag); + } catch (IOException e) { + Log.e(LOG_TAG, safeMessage(e)); + } + } + + private void recordTestTime() throws IOException { + if(!timeAlreadyWritten) { + timeAlreadyWritten = true; + mSerializer.attribute("", ATTRIBUTE_TIME, + String.format("%.3f", (System.currentTimeMillis() - testStart) / 1000.)); + } + } + + @Override + public void endTest(Test test) { + try { + if (test instanceof TestCase) { + recordTestTime(); + mSerializer.endTag("", TAG_CASE); + } + } catch (IOException e) { + Log.e(LOG_TAG, safeMessage(e)); + } + } + + /** + * Releases all resources associated with this listener. Must be called + * when the listener is finished with. + */ + public void close() { + if (mSerializer != null) { + try { + if (mCurrentSuite != null) { + mSerializer.endTag("", TAG_SUITE); + } + + mSerializer.endTag("", TAG_SUITES); + mSerializer.endDocument(); + mSerializer = null; + } catch (IOException e) { + Log.e(LOG_TAG, safeMessage(e)); + } + } + + if (mOutputStream != null) { + try { + mOutputStream.close(); + mOutputStream = null; + } catch (IOException e) { + Log.e(LOG_TAG, safeMessage(e)); + } + } + } + + private String safeMessage(Throwable error) { + String message = error.getMessage(); + return error.getClass().getName() + ": " + (message == null ? "" : message); + } + + /** + * Wrapper around a print writer that filters out common noise from stack + * traces, making it easier to see the actual failure. + */ + private static class FilteringWriter extends PrintWriter { + public FilteringWriter(Writer out) { + super(out); + } + + @Override + public void println(String s) { + for (String filtered : DEFAULT_TRACE_FILTERS) { + if (s.contains(filtered)) { + return; + } + } + + super.println(s); + } + } +} diff --git a/tests/src/com/zutubi/android/junitreport/JUnitReportTestRunner.java b/tests/src/com/zutubi/android/junitreport/JUnitReportTestRunner.java new file mode 100644 index 000000000..863e17f65 --- /dev/null +++ b/tests/src/com/zutubi/android/junitreport/JUnitReportTestRunner.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Zutubi Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zutubi.android.junitreport; + +import android.os.Bundle; +import android.test.AndroidTestRunner; +import android.test.InstrumentationTestRunner; + +/** + * Custom test runner that adds a {@link JUnitReportListener} to the underlying + * test runner in order to capture test results in an XML report. You may use + * this class in place of {@link InstrumentationTestRunner} in your test + * project's manifest, and/or specify it to your Ant build using the test.runner + * property. + *

    + * This runner behaves identically to the default, with the added side-effect of + * producing a JUnit XML report. The report format is similar to that produced + * by the Ant JUnit task's XML formatter, making it compatible with existing + * tools that can process that format. See {@link JUnitReportListener} for + * further details. + *

    + * This runner accepts the following arguments: + *

      + *
    • reportFilePath: path of the file to write the XML report to, in the + * target application's data area (default: junit-report.xml).
    • + *
    • filterTraces: if true, stack traces in test failure reports will be + * filtered to remove noise such as framework methods (default: true)
    • + *
    + * These arguments may be specified as follows: + * + *
    + * {@code adb shell am instrument -w -e reportFile my-report-file.xml}
    + * 
    + */ +public class JUnitReportTestRunner extends InstrumentationTestRunner { + /** + * Path, relative to the target applications file root, at which to write the report file. + */ + private static final String ARG_REPORT_FILE_PATH = "reportFilePath"; + /** + * If true, stack traces in the report will be filtered to remove common noise (e.g. framework + * methods). + */ + private static final String ARG_FILTER_TRACES = "filterTraces"; + /** + * Default path of the report file. + */ + private static final String DEFAULT_REPORT_FILE = "junit-report.xml"; + + private JUnitReportListener mListener; + private String mReportFilePath; + private boolean mFilterTraces = true; + + @Override + public void onCreate(Bundle arguments) { + if (arguments != null) { + mReportFilePath = arguments.getString(ARG_REPORT_FILE_PATH); + mFilterTraces = arguments.getBoolean(ARG_FILTER_TRACES, true); + } + + if (mReportFilePath == null) { + mReportFilePath = DEFAULT_REPORT_FILE; + } + + super.onCreate(arguments); + } + + @Override + protected AndroidTestRunner getAndroidTestRunner() { + AndroidTestRunner runner = new AndroidTestRunner(); + mListener = new JUnitReportListener(getTargetContext(), mReportFilePath, mFilterTraces); + runner.addTestListener(mListener); + return runner; + } + + @Override + public void finish(int resultCode, Bundle results) { + if (mListener != null) { + mListener.close(); + } + + super.finish(resultCode, results); + } +} From 951baa052bf54b162cc21d54af5a28c9d01d7ee1 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 27 Sep 2010 16:37:21 -0700 Subject: [PATCH 04/12] handle case where auto-gen resource not an integer --- .../com/todoroo/andlib/test/TranslationTests.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/src/com/todoroo/andlib/test/TranslationTests.java b/tests/src/com/todoroo/andlib/test/TranslationTests.java index afed42696..c5041320b 100644 --- a/tests/src/com/todoroo/andlib/test/TranslationTests.java +++ b/tests/src/com/todoroo/andlib/test/TranslationTests.java @@ -4,8 +4,10 @@ package com.todoroo.andlib.test; import java.lang.reflect.Field; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Locale; import android.content.res.Configuration; @@ -265,11 +267,18 @@ abstract public class TranslationTests extends TodorooTestCase { */ public int[] getResourceIds(Class resources) throws Exception { Field[] fields = resources.getDeclaredFields(); - int[] ids = new int[fields.length]; + List ids = new ArrayList(fields.length); for(int i = 0; i < fields.length; i++) { - ids[i] = fields[i].getInt(null); + try { + ids.add(fields[i].getInt(null)); + } catch (Exception e) { + // not a field we care about + } } - return ids; + int[] idsAsIntArray = new int[ids.size()]; + for(int i = 0; i < ids.size(); i++) + idsAsIntArray[i] = ids.get(i); + return idsAsIntArray; } /** From 14819318e3937a399406863da2bf66a7d2aa6cd9 Mon Sep 17 00:00:00 2001 From: Arne Jans Date: Sat, 25 Sep 2010 06:47:17 +0800 Subject: [PATCH 05/12] Added timezone-selection to user-signup --- astrid/gen/.gitignore | 0 .../producteev/ProducteevLoginActivity.java | 8 +- .../producteev/api/ProducteevInvoker.java | 3 +- .../res/layout/producteev_login_activity.xml | 135 +++----- astrid/res/values/strings-producteev.xml | 3 + astrid/res/values/timezones-producteev.xml | 316 ++++++++++++++++++ 6 files changed, 380 insertions(+), 85 deletions(-) delete mode 100644 astrid/gen/.gitignore create mode 100644 astrid/res/values/timezones-producteev.xml diff --git a/astrid/gen/.gitignore b/astrid/gen/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevLoginActivity.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevLoginActivity.java index dcc489d03..4913e07b6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevLoginActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevLoginActivity.java @@ -29,6 +29,7 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; +import android.widget.Spinner; import android.widget.TextView; import com.flurry.android.FlurryAgent; @@ -111,6 +112,7 @@ public class ProducteevLoginActivity extends Activity { Editable password = passwordEditText.getText(); Editable firstName = ((EditText)findViewById(R.id.firstName)).getText(); Editable lastName = ((EditText)findViewById(R.id.lastName)).getText(); + String timezone = ((Spinner)findViewById(R.id.timezoneList)).getSelectedItem().toString(); if(email.length() == 0 || password.length() == 0 || firstName.length() == 0 || lastName.length() == 0) { @@ -119,7 +121,7 @@ public class ProducteevLoginActivity extends Activity { return; } performSignup(email.toString(), password.toString(), - firstName.toString(), lastName.toString()); + firstName.toString(), lastName.toString(), timezone); } } }); @@ -167,7 +169,7 @@ public class ProducteevLoginActivity extends Activity { } private void performSignup(final String email, final String password, - final String firstName, final String lastName) { + final String firstName, final String lastName, final String timezone) { final ProgressDialog dialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_wait)); final TextView errors = (TextView) findViewById(R.id.error); @@ -178,7 +180,7 @@ public class ProducteevLoginActivity extends Activity { ProducteevInvoker invoker = ProducteevSyncProvider.getInvoker(); final StringBuilder errorMessage = new StringBuilder(); try { - invoker.usersSignUp(email, firstName, lastName, password, null); + invoker.usersSignUp(email, firstName, lastName, password, timezone, null); invoker.authenticate(email, password); Preferences.setString(R.string.producteev_PPr_email, email); diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java b/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java index 349ca292a..3377afd41 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/api/ProducteevInvoker.java @@ -86,12 +86,13 @@ public class ProducteevInvoker { * Sign up as the given user */ public JSONObject usersSignUp(String email, String firstName, String lastName, String - password, Long fbUid) throws IOException, ApiServiceException { + password, String timezone, Long fbUid) throws IOException, ApiServiceException { return invokeGet("users/signup.json", "email", email, "firstname", firstName, "lastname", lastName, "password", password, + "timezone", timezone, "fbuid", fbUid); } diff --git a/astrid/res/layout/producteev_login_activity.xml b/astrid/res/layout/producteev_login_activity.xml index ebf13ea1e..efe102f2e 100644 --- a/astrid/res/layout/producteev_login_activity.xml +++ b/astrid/res/layout/producteev_login_activity.xml @@ -1,77 +1,55 @@ - - - - - - - - + + + + + + + + - - - + android:inputType="textEmailAddress" /> + + + + - - - - - - - - + android:orientation="vertical" android:paddingTop="20dip" + android:visibility="gone"> + + + + + + + + + +