aboutsummaryrefslogtreecommitdiff
path: root/libs/wpcomrest
diff options
context:
space:
mode:
Diffstat (limited to 'libs/wpcomrest')
-rw-r--r--libs/wpcomrest/.gitignore25
-rw-r--r--libs/wpcomrest/LICENSE-GPL279
-rw-r--r--libs/wpcomrest/LICENSE-MIT21
-rw-r--r--libs/wpcomrest/README.md26
-rw-r--r--libs/wpcomrest/WordPressComRest/build.gradle31
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/AndroidManifest.xml22
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivity.java91
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivityTest.java44
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/NotesActivity.java183
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/WPClient.java35
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/OauthTest.java32
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/RestClientTest.java26
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/util/TestExecutorService.java20
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-ldpi/ic_launcher.pngbin0 -> 2729 bytes
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/login.xml23
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/main.xml16
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/note.xml25
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/raw/oauth.properties3
-rw-r--r--libs/wpcomrest/WordPressComRest/src/androidTest/res/values/strings.xml8
-rw-r--r--libs/wpcomrest/WordPressComRest/src/main/AndroidManifest.xml8
-rw-r--r--libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/Oauth.java168
-rw-r--r--libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestClient.java101
-rw-r--r--libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestRequest.java83
-rw-r--r--libs/wpcomrest/build.gradle0
-rw-r--r--libs/wpcomrest/gradle/wrapper/gradle-wrapper.jarbin0 -> 51348 bytes
-rw-r--r--libs/wpcomrest/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xlibs/wpcomrest/gradlew164
-rw-r--r--libs/wpcomrest/gradlew.bat90
-rw-r--r--libs/wpcomrest/settings.gradle1
-rw-r--r--libs/wpcomrest/test/.gitignore3
-rw-r--r--libs/wpcomrest/test/ant.properties18
-rw-r--r--libs/wpcomrest/test/app/.gitignore3
-rw-r--r--libs/wpcomrest/test/app/AndroidManifest.xml17
-rw-r--r--libs/wpcomrest/test/app/ant.properties17
-rw-r--r--libs/wpcomrest/test/app/build.xml92
-rw-r--r--libs/wpcomrest/test/app/proguard-project.txt20
-rw-r--r--libs/wpcomrest/test/app/project.properties15
-rw-r--r--libs/wpcomrest/test/build.xml92
-rw-r--r--libs/wpcomrest/test/proguard-project.txt20
-rw-r--r--libs/wpcomrest/test/project.properties14
-rw-r--r--libs/wpcomrest/tools/deploy-mvn-artifact.conf-example1
-rwxr-xr-xlibs/wpcomrest/tools/deploy-mvn-artifact.sh19
45 files changed, 1862 insertions, 0 deletions
diff --git a/libs/wpcomrest/.gitignore b/libs/wpcomrest/.gitignore
new file mode 100644
index 000000000..8babf679a
--- /dev/null
+++ b/libs/wpcomrest/.gitignore
@@ -0,0 +1,25 @@
+# generated files
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+tools/deploy-mvn-artifact.conf
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Gradle
+.gradle/
+gradle.properties
+
+# Idea
+.idea/workspace.xml
+*.iml
+
+# OS X
+.DS_Store
+
+# dependencies
diff --git a/libs/wpcomrest/LICENSE-GPL b/libs/wpcomrest/LICENSE-GPL
new file mode 100644
index 000000000..356777934
--- /dev/null
+++ b/libs/wpcomrest/LICENSE-GPL
@@ -0,0 +1,279 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/libs/wpcomrest/LICENSE-MIT b/libs/wpcomrest/LICENSE-MIT
new file mode 100644
index 000000000..63ecc8b97
--- /dev/null
+++ b/libs/wpcomrest/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Automattic Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/libs/wpcomrest/README.md b/libs/wpcomrest/README.md
new file mode 100644
index 000000000..23db4c914
--- /dev/null
+++ b/libs/wpcomrest/README.md
@@ -0,0 +1,26 @@
+# WordPress REST Client for Android
+
+## Build
+
+To build the library, invoke the following `gradle` command in the project root directory:
+
+ $ ./gradlew build
+
+This will create an `aar` package at this location: `WordPressComRest/build/outputs/aar/WordPressComRest.aar`. Feel free to use it directly or put it in a maven repository.
+
+## Usage
+
+If you don't want to compile and host it. The easiest way to use it in your Android project is to add it as a library in your build.gradle file, don't forget to add the wordpress-mobile maven repository. For instance:
+
+ repositories {
+ maven { url 'http://wordpress-mobile.github.io/WordPress-Android' }
+ }
+
+ dependencies {
+ // use the latest 1.x version
+ compile 'com.automattic:wordpresscom-rest:1.+'
+ }
+
+## LICENSE
+
+This library is dual licensed unded MIT and GPL v2.
diff --git a/libs/wpcomrest/WordPressComRest/build.gradle b/libs/wpcomrest/WordPressComRest/build.gradle
new file mode 100644
index 000000000..8fa117bcf
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies { classpath 'com.android.tools.build:gradle:0.12.+' }
+}
+
+apply plugin: 'com.android.library'
+
+repositories {
+ mavenCentral()
+}
+
+android {
+ defaultPublishConfig 'debug'
+
+ compileSdkVersion 20
+ buildToolsVersion "19.1.0"
+
+ defaultConfig {
+ applicationId "com.wordpress.rest"
+ versionName "1.0.0"
+ versionCode 1
+ minSdkVersion 14
+ targetSdkVersion 20
+ }
+}
+
+dependencies {
+ compile 'com.mcxiaoke.volley:library:1.0.+'
+}
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/AndroidManifest.xml b/libs/wpcomrest/WordPressComRest/src/androidTest/AndroidManifest.xml
new file mode 100644
index 000000000..bb23b43d9
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.wordpress.notereader.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <uses-permission android:name="android.permission.INTERNET" />
+ <!--
+ This declares that this application uses the instrumentation test runner targeting
+ the package of com.wordpress.notereader. To run the tests use the command:
+ "adb shell am instrument -w com.wordpress.notereader.tests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.wordpress.notereader"
+ android:label="Tests for com.wordpress.notereader"/>
+</manifest>
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivity.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivity.java
new file mode 100644
index 000000000..07a0058e3
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivity.java
@@ -0,0 +1,91 @@
+package com.wordpress.notereader;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.content.Intent;
+
+import android.widget.Button;
+import android.widget.EditText;
+
+import android.util.Log;
+import android.view.View;
+
+import com.wordpress.rest.Oauth;
+import com.wordpress.rest.OauthToken;
+import com.wordpress.rest.OauthTokenResponseHandler;
+
+import java.util.Properties;
+import java.io.InputStream;
+
+import org.json.JSONObject;
+
+public class LoginActivity extends Activity {
+
+ private static final String OAUTH_ID_NAME="oauth.appid";
+ private static final String OAUTH_SECRET_NAME="oauth.appsecret";
+ private static final String OAUTH_REDIRECT_URI="oauth.redirect_uri";
+ public static final String OAUTH_TOKEN_EXTRA="oauth-access-token";
+ private static final String TAG="NotesLogin";
+ private Properties mConfig;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+ getConfigProperties();
+
+ setContentView(R.layout.login);
+
+ final Oauth oauth = new Oauth(
+ mConfig.getProperty(OAUTH_ID_NAME),
+ mConfig.getProperty(OAUTH_SECRET_NAME),
+ mConfig.getProperty(OAUTH_REDIRECT_URI)
+ );
+
+ Button button = (Button) findViewById(R.id.signin_button);
+ final EditText usernameField = (EditText) findViewById(R.id.username);
+ final EditText passwordField = (EditText) findViewById(R.id.password);
+
+ button.setOnClickListener( new View.OnClickListener(){
+ public void onClick(View v){
+ oauth.requestAccessToken(
+ usernameField.getText().toString(),
+ passwordField.getText().toString(),
+ new OauthTokenResponseHandler(){
+ @Override
+ public void onSuccess(final OauthToken token){
+ runOnUiThread(new Runnable(){
+ @Override
+ public void run(){
+ Intent result = new Intent();
+ result.putExtra(OAUTH_TOKEN_EXTRA, token.toString());
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ }
+ });
+ }
+ @Override
+ public void onFailure(Throwable e, JSONObject response){
+ Log.d(TAG, String.format("Failed %s", response));
+ }
+ }
+ );
+ }
+ });
+
+ }
+
+ protected Properties getConfigProperties(){
+ if (mConfig == null) {
+ mConfig = new Properties();
+ InputStream stream = getResources().openRawResource(R.raw.oauth);
+ try {
+ mConfig.load(stream);
+ } catch(java.io.IOException e){
+ mConfig = null;
+ Log.e(TAG, "Could not load config", e);
+ }
+ }
+ return mConfig;
+ }
+
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivityTest.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivityTest.java
new file mode 100644
index 000000000..8401a3b56
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/LoginActivityTest.java
@@ -0,0 +1,44 @@
+package com.wordpress.notereader;
+
+// import com.wordpress.notereader.R;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+
+import android.widget.EditText;
+import android.widget.Button;
+
+public class LoginActivityTest extends ActivityInstrumentationTestCase2<LoginActivity> {
+
+ private LoginActivity mActivity;
+
+ public LoginActivityTest(){
+ super("com.wordpress.notereader", LoginActivity.class);
+ }
+
+ public void setUp(){
+ setActivityInitialTouchMode(false);
+ mActivity = getActivity();
+ }
+
+ public void testInitialViewState(){
+ EditText usernameField = (EditText) mActivity.findViewById(R.id.username);
+ Button signInButton = (Button) mActivity.findViewById(R.id.signin_button);
+
+ assertEquals("", usernameField.getText().toString());
+ assertEquals("Sign In", signInButton.getText().toString());
+ }
+
+ @UiThreadTest
+ public void testLogin(){
+ EditText usernameField = (EditText) mActivity.findViewById(R.id.username);
+ EditText passwordField = (EditText) mActivity.findViewById(R.id.password);
+ Button signInButton = (Button) mActivity.findViewById(R.id.signin_button);
+
+ usernameField.setText("mobiletestuser");
+ passwordField.setText("password");
+
+ signInButton.performClick();
+
+ }
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/NotesActivity.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/NotesActivity.java
new file mode 100644
index 000000000..450df9227
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/NotesActivity.java
@@ -0,0 +1,183 @@
+package com.wordpress.notereader;
+
+import static com.wordpress.notereader.WPClient.*;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.loopj.android.http.JsonHttpResponseHandler;
+
+import android.content.SharedPreferences;
+import android.app.ListActivity;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import android.text.Html;
+
+public class NotesActivity extends ListActivity
+{
+ private static final int LOGIN_REQUEST_CODE=0xCC;
+ private static final String PREFERENCES_NAME="account-prefs";
+ private static final String OAUTH_TOKEN_PREFERENCE="oauth-access-token";
+ public static final String TAG="WPNotes";
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ loadAccessTokenFromPreferences();
+ setContentView(R.layout.main);
+ setListAdapter(new NotesAdapter(this));
+ }
+
+ @Override
+ public void onStart(){
+ super.onStart();
+ if (!isAuthenticated()) {
+ startLoginActivity();
+ } else {
+ refreshNotifications();
+ }
+ }
+
+ public void startLoginActivity(){
+ Intent intent = new Intent(this, LoginActivity.class);
+ startActivityForResult(intent, LOGIN_REQUEST_CODE);
+ }
+
+ private void refreshNotifications(){
+ get("notifications", new NotesResponseHandler());
+ }
+
+ private NotesAdapter getNotesAdapter(){
+ return (NotesAdapter) getListAdapter();
+ }
+
+ private class NotesAdapter extends ArrayAdapter<Note> {
+ private static final int LAYOUT_ID=R.layout.note;
+ private static final int VIEW_ID=R.id.note_label;
+ public NotesAdapter(Context context){
+ super(context, LAYOUT_ID, VIEW_ID);
+ }
+
+ public void bindView(int position, View view){
+ Log.d(TAG, String.format("Bind view for %d", position));
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ View view = super.getView(position, convertView, parent);
+
+ return view;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data){
+ Log.d(TAG, String.format("Activity finished %d with result %d", requestCode, resultCode));
+ switch (requestCode) {
+ case LOGIN_REQUEST_CODE:
+ Log.d(TAG, String.format("Was result (%d) ok? %d", resultCode, Activity.RESULT_OK));
+ if (resultCode == RESULT_OK) {
+ String accessToken = data.getStringExtra(LoginActivity.OAUTH_TOKEN_EXTRA);
+ setAccessToken(accessToken);
+ saveAccessTokenToPreferences(accessToken);
+ refreshNotifications();
+ }
+ break;
+
+ }
+ }
+
+ private void loadAccessTokenFromPreferences(){
+ SharedPreferences prefs = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
+ String token = prefs.getString(OAUTH_TOKEN_PREFERENCE, null);
+ Log.d(TAG, String.format("Retrieved access token: %s", token));
+ setAccessToken(token);
+ }
+
+ private void saveAccessTokenToPreferences(String token){
+ Log.d(TAG, String.format("Saving access token %s", token));
+ SharedPreferences.Editor prefs = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE).edit();
+ prefs.putString(OAUTH_TOKEN_PREFERENCE, token);
+ prefs.commit();
+ }
+
+ private class NotesResponseHandler extends JsonHttpResponseHandler {
+ private static final String NOTES_KEY="notes";
+
+ @Override
+ public void onStart(){
+ Log.d(TAG, "Fetching notes");
+ }
+ @Override
+ public void onFinish(){
+ Log.d(TAG, "Finished notes request");
+ }
+ @Override
+ public void onFailure(Throwable error, JSONObject response){
+ Log.e(TAG, String.format("Failed to retrieve notes: %s"), error);
+ }
+
+ @Override
+ public void onFailure(Throwable error, String response){
+ Log.e(TAG, String.format("Failed: %s", response), error);
+ }
+
+ @Override
+ public void onSuccess(int statuScode, JSONObject notes){
+ try {
+ final JSONArray notesArray = notes.getJSONArray(NOTES_KEY);
+ getNotesAdapter().clear();
+ for (int i=0;i<notesArray.length();i++) {
+ JSONObject noteJson = notesArray.getJSONObject(i);
+ getNotesAdapter().add(new Note(noteJson));
+ }
+
+ runOnUiThread( new Runnable(){
+ @Override
+ public void run(){
+ getNotesAdapter().notifyDataSetChanged();
+ }
+ });
+
+ } catch (JSONException e) {
+ Log.e(TAG, "Couldn't retrieve notes", e);
+ }
+ }
+
+ }
+
+ private class Note {
+
+ private static final String SUBJECT_KEY="subject";
+ private static final String SUBJECT_TEXT_KEY="text";
+ private static final String UNKNOWN_SUBJECT="no subject";
+
+ private JSONObject mNoteData;
+
+ public Note(JSONObject noteData){
+ mNoteData = noteData;
+ }
+
+ public String toString(){
+ // try to get the subject.text property
+ try {
+ JSONObject subject = mNoteData.getJSONObject(SUBJECT_KEY);
+ String subjectText = subject.getString(SUBJECT_TEXT_KEY);
+ return Html.fromHtml(subjectText.trim()).toString();
+ } catch (JSONException error) {
+ return UNKNOWN_SUBJECT;
+ }
+ }
+
+ }
+
+}
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/WPClient.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/WPClient.java
new file mode 100644
index 000000000..a1274f213
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/notereader/WPClient.java
@@ -0,0 +1,35 @@
+package com.wordpress.notereader;
+
+import com.wordpress.rest.RestClient;
+import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.loopj.android.http.RequestParams;
+
+public class WPClient {
+
+ public static RestClient restClient = new RestClient();
+
+ public static void setAccessToken(String token){
+ restClient.setAccessToken(token);
+ }
+
+ public static boolean isAuthenticated(){
+ return restClient.isAuthenticated();
+ }
+
+ public static void get(String path, AsyncHttpResponseHandler handler){
+ restClient.get(path, handler);
+ }
+
+ public static void get(String path, RequestParams params, AsyncHttpResponseHandler handler){
+ restClient.get(path, params, handler);
+ }
+
+ public static void notifications(AsyncHttpResponseHandler handler){
+ notifications(null, handler);
+ }
+
+ public static void notifications(RequestParams params, AsyncHttpResponseHandler handler){
+ get("notifications", params, handler);
+ }
+
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/OauthTest.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/OauthTest.java
new file mode 100644
index 000000000..2958b8934
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/OauthTest.java
@@ -0,0 +1,32 @@
+package com.wordpress.rest;
+
+import android.test.AndroidTestCase;
+import com.wordpress.rest.Oauth;
+
+import com.wordpress.util.TestExecutorService;
+
+import android.util.Log;
+
+public class OauthTest extends AndroidTestCase {
+
+ public final String TAG="WordPressTest";
+
+ private String mAppId;
+ private String mAppSecret;
+ private String mAppRedirectURI;
+ private Oauth mClient;
+
+ @Override
+ public void setUp(){
+ mClient = new Oauth(mAppId, mAppSecret, mAppRedirectURI);
+ }
+
+ public void testRequestAuthorizationURL(){
+ String url = mClient.getAuthorizationURL();
+
+ String expected = String.format("https://public-api.wordpress.com/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code", mClient.getAppID(), mClient.getAppSecret(), mClient.getAppRedirectURI());
+ assertEquals(expected, url);
+ }
+
+
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/RestClientTest.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/RestClientTest.java
new file mode 100644
index 000000000..7fa2053f1
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/rest/RestClientTest.java
@@ -0,0 +1,26 @@
+package com.wordpress.rest;
+
+import android.test.AndroidTestCase;
+
+public class RestClientTest extends AndroidTestCase {
+
+ public void testGetAbsoluteURLWithPath(){
+ String path = "me";
+ String url = RestClient.getAbsoluteURL(path);
+ String expected = String.format("%s%s", RestClient.REST_API_ENDPOINT_URL, path);
+ assertEquals(expected, url);
+ }
+
+ public void testGetAbsoluteURLWithLeadingSlash(){
+ String path = "/sites/mobileprojects.wordpress.com/posts";
+ String url = RestClient.getAbsoluteURL(path);
+ String expected = String.format("https://public-api.wordpress.com/rest/v1%s", path);
+ assertEquals(expected, url);
+ }
+
+ public void testGetAbsoluteURLWithFullURL(){
+ String expected = String.format("%s%s", RestClient.REST_API_ENDPOINT_URL, "notes");
+ assertEquals(expected, RestClient.getAbsoluteURL(expected));
+ }
+
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/util/TestExecutorService.java b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/util/TestExecutorService.java
new file mode 100644
index 000000000..0ff3189e3
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/java/src/com/wordpress/util/TestExecutorService.java
@@ -0,0 +1,20 @@
+package com.wordpress.util;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.Future;
+import java.util.concurrent.BlockingQueue;
+
+public class TestExecutorService extends ThreadPoolExecutor {
+ public TestExecutorService(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+ }
+
+ @Override
+ public Future<?> submit(Runnable runnable) {
+ FutureTask futureTask = new FutureTask(runnable, null);
+ futureTask.run(); // or queue this future to be run when you want it to be run
+ return futureTask;
+ }
+}
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-hdpi/ic_launcher.png b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-ldpi/ic_launcher.png b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..99238729d
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-mdpi/ic_launcher.png b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-xhdpi/ic_launcher.png b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/login.xml b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/login.xml
new file mode 100644
index 000000000..d38f3cc88
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/login.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ >
+ <EditText android:id="@+id/username"
+ android:hint="@string/username_hint"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+ <EditText android:id="@+id/password"
+ android:hint="@string/password_hint"
+ android:password="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp" />
+ <Button android:id="@+id/signin_button"
+ android:text="@string/signin_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
+
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/main.xml b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/main.xml
new file mode 100644
index 000000000..6bf742217
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/main.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
+ <TextView
+ android:id="@android:id/empty"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/no_notifications" />
+</LinearLayout>
+
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/note.xml b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/note.xml
new file mode 100644
index 000000000..4fab73b9f
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/layout/note.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="8sp"
+ >
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/note_image"
+ android:layout_width="48sp"
+ android:layout_height="48sp"
+ android:scaleType="fitCenter" />
+ <TextView
+ android:id="@+id/note_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:gravity="fill" />
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/raw/oauth.properties b/libs/wpcomrest/WordPressComRest/src/androidTest/res/raw/oauth.properties
new file mode 100644
index 000000000..fa5b5d7b2
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/raw/oauth.properties
@@ -0,0 +1,3 @@
+oauth.appid=11
+oauth.appsecret=zf66wMPjz0BNohvOLsfYGyM2YbxwgVEVyiXXQDcVwzK73JDOPPuhrvCuoln0Ul9o
+oauth.redirect_uri=https://wordpress.com/ \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/androidTest/res/values/strings.xml b/libs/wpcomrest/WordPressComRest/src/androidTest/res/values/strings.xml
new file mode 100644
index 000000000..f8f6bccd9
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/androidTest/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Notes</string>
+ <string name="signin_label">Sign In</string>
+ <string name="username_hint">Username</string>
+ <string name="password_hint">Password</string>
+ <string name="no_notifications">No notifications</string>
+</resources>
diff --git a/libs/wpcomrest/WordPressComRest/src/main/AndroidManifest.xml b/libs/wpcomrest/WordPressComRest/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..95a7bbddc
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.wordpress.rest"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="3" />
+ <uses-permission android:name="android.permission.INTERNET" />
+</manifest>
diff --git a/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/Oauth.java b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/Oauth.java
new file mode 100644
index 000000000..250921772
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/Oauth.java
@@ -0,0 +1,168 @@
+package com.wordpress.rest;
+
+import com.android.volley.NetworkResponse;
+import com.android.volley.ParseError;
+import com.android.volley.Response;
+import com.android.volley.toolbox.HttpHeaderParser;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Oauth {
+ public static final String TAG = "WordPressREST";
+
+ public static final String AUTHORIZE_ENDPOINT = "https://public-api.wordpress.com/oauth2/authorize";
+ private static final String AUTHORIZED_ENDPOINT_FORMAT = "%s?client_id=%s&redirect_uri=%s&response_type=code";
+
+ public static final String TOKEN_ENDPOINT = "https://public-api.wordpress.com/oauth2/token";
+
+ public static final String CLIENT_ID_PARAM_NAME = "client_id";
+ public static final String REDIRECT_URI_PARAM_NAME = "redirect_uri";
+ public static final String CLIENT_SECRET_PARAM_NAME = "client_secret";
+ public static final String CODE_PARAM_NAME = "code";
+ public static final String GRANT_TYPE_PARAM_NAME = "grant_type";
+ public static final String USERNAME_PARAM_NAME = "username";
+ public static final String PASSWORD_PARAM_NAME = "password";
+
+ public static final String PASSWORD_GRANT_TYPE = "password";
+ public static final String BEARER_GRANT_TYPE = "bearer";
+ public static final String AUTHORIZATION_CODE_GRANT_TYPE = "authorization_code";
+
+ private String mAppId;
+ private String mAppSecret;
+ private String mAppRedirectURI;
+
+ public interface Listener extends Response.Listener<Token> {
+ }
+ public interface ErrorListener extends Response.ErrorListener {
+ }
+
+ public Oauth(String appId, String appSecret, String redirectURI) {
+ mAppId = appId;
+ mAppSecret = appSecret;
+ mAppRedirectURI = redirectURI;
+ }
+
+ public String getAppID() {
+ return mAppId;
+ }
+
+ public String getAppSecret() {
+ return mAppSecret;
+ }
+
+ public String getAppRedirectURI() {
+ return mAppRedirectURI;
+ }
+
+ public String getAuthorizationURL() {
+ return String.format(AUTHORIZED_ENDPOINT_FORMAT, AUTHORIZE_ENDPOINT, getAppID(), getAppRedirectURI());
+ }
+
+ public Request makeRequest(String username, String password, Listener listener, ErrorListener errorListener) {
+ return new PasswordRequest(getAppID(), getAppSecret(), getAppRedirectURI(), username, password, listener,
+ errorListener);
+ }
+
+ public Request makeRequest(String code, Listener listener, ErrorListener errorListener) {
+ return new BearerRequest(getAppID(), getAppSecret(), getAppRedirectURI(), code, listener, errorListener);
+ }
+
+ private static class Request extends com.android.volley.Request<Token> {
+ private final Listener mListener;
+ protected Map<String, String> mParams = new HashMap<String, String>();
+
+ Request(String appId, String appSecret, String redirectUri, Listener listener, ErrorListener errorListener) {
+ super(Method.POST, TOKEN_ENDPOINT, errorListener);
+ mListener = listener;
+ mParams.put(CLIENT_ID_PARAM_NAME, appId);
+ mParams.put(CLIENT_SECRET_PARAM_NAME, appSecret);
+ mParams.put(REDIRECT_URI_PARAM_NAME, redirectUri);
+ }
+
+ @Override
+ public Map<String, String> getParams() {
+ return mParams;
+ }
+
+ @Override
+ public void deliverResponse(Token token) {
+ mListener.onResponse(token);
+ }
+
+ @Override
+ protected Response<Token> parseNetworkResponse(NetworkResponse response) {
+ try {
+ String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
+ JSONObject tokenData = new JSONObject(jsonString);
+ return Response.success(Token.fromJSONObject(tokenData), HttpHeaderParser.parseCacheHeaders(response));
+ } catch (UnsupportedEncodingException e) {
+ return Response.error(new ParseError(e));
+ } catch (JSONException je) {
+ return Response.error(new ParseError(je));
+ }
+ }
+ }
+
+ public static class PasswordRequest extends Request {
+
+ public PasswordRequest(String appId, String appSecret, String redirectUri, String username, String password,
+ Listener listener, ErrorListener errorListener) {
+ super(appId, appSecret, redirectUri, listener, errorListener);
+ mParams.put(USERNAME_PARAM_NAME, username);
+ mParams.put(PASSWORD_PARAM_NAME, password);
+ mParams.put(GRANT_TYPE_PARAM_NAME, PASSWORD_GRANT_TYPE);
+ }
+ }
+
+ public static class BearerRequest extends Request {
+
+ public BearerRequest(String appId, String appSecret, String redirectUri, String code, Listener listener,
+ ErrorListener errorListener) {
+ super(appId, appSecret, redirectUri, listener, errorListener);
+ mParams.put(CODE_PARAM_NAME, code);
+ mParams.put(GRANT_TYPE_PARAM_NAME, BEARER_GRANT_TYPE);
+ }
+ }
+
+ public static class Token {
+
+ private static final String TOKEN_TYPE_FIELD_NAME = "token_type";
+ private static final String ACCESS_TOKEN_FIELD_NAME = "access_token";
+ private static final String BLOG_URL_FIELD_NAME = "blog_url";
+ private static final String SCOPE_FIELD_NAME = "scope";
+ private static final String BLOG_ID_FIELD_NAME = "blog_id";
+
+ private String mTokenType;
+ private String mScope;
+ private String mAccessToken;
+ private String mBlogUrl;
+ private String mBlogId;
+
+ public Token(String accessToken, String blogUrl, String blogId, String scope, String tokenType) {
+ mAccessToken = accessToken;
+ mBlogUrl = blogUrl;
+ mBlogId = blogId;
+ mScope = scope;
+ mTokenType = tokenType;
+ }
+
+ public String getAccessToken() {
+ return mAccessToken;
+ }
+
+ public String toString() {
+ return getAccessToken();
+ }
+
+ public static Token fromJSONObject(JSONObject tokenJSON) throws JSONException {
+ return new Token(tokenJSON.getString(ACCESS_TOKEN_FIELD_NAME), tokenJSON.getString(BLOG_URL_FIELD_NAME),
+ tokenJSON.getString(BLOG_ID_FIELD_NAME), tokenJSON.getString(SCOPE_FIELD_NAME), tokenJSON.getString(
+ TOKEN_TYPE_FIELD_NAME));
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestClient.java b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestClient.java
new file mode 100644
index 000000000..5320f679c
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestClient.java
@@ -0,0 +1,101 @@
+package com.wordpress.rest;
+
+import com.android.volley.Request.Method;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response.ErrorListener;
+import com.android.volley.Response.Listener;
+
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Map;
+
+public class RestClient {
+ public static final String TAG = "WordPressREST";
+ public static final String REST_API_ENDPOINT_URL = "https://public-api.wordpress.com/rest/v1/";
+ public static final String PARAMS_ENCODING = "UTF-8";
+
+ private RequestQueue mQueue;
+ private String mAccessToken;
+ private String mUserAgent;
+
+ public RestClient(RequestQueue queue) {
+ mQueue = queue;
+ }
+
+ public RestClient(RequestQueue queue, String token) {
+ this(queue);
+ mAccessToken = token;
+ }
+
+ public RestRequest get(String path, Listener<JSONObject> listener, ErrorListener errorListener) {
+ return makeRequest(Method.GET, getAbsoluteURL(path), null, listener, errorListener);
+ }
+
+ public RestRequest post(String path, Map<String, String> body, Listener<JSONObject> listener,
+ ErrorListener errorListener) {
+ return makeRequest(Method.POST, getAbsoluteURL(path), body, listener, errorListener);
+ }
+
+ public RestRequest makeRequest(int method, String url, Map<String, String> params, Listener<JSONObject> listener,
+ ErrorListener errorListener) {
+ RestRequest request = new RestRequest(method, url, params, listener, errorListener);
+ request.setUserAgent(mUserAgent);
+ request.setAccessToken(mAccessToken);
+ return request;
+ }
+
+ public RestRequest send(RestRequest request) {
+ // Volley send the request
+ mQueue.add(request);
+ return request;
+ }
+
+ public static String getAbsoluteURL(String url) {
+ // if it already starts with our endpoint, let it pass through
+ if (url.indexOf(REST_API_ENDPOINT_URL) == 0) {
+ return url;
+ }
+ // if it has a leading slash, remove it
+ if (url.indexOf("/") == 0) {
+ url = url.substring(1);
+ }
+ // prepend the endpoint
+ return String.format("%s%s", REST_API_ENDPOINT_URL, url);
+ }
+
+ public static String getAbsoluteURL(String path, Map<String, String> params) {
+ String url = getAbsoluteURL(path);
+ if (params != null) {
+ // build a query string
+ StringBuilder query = new StringBuilder();
+ try {
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ query.append(URLEncoder.encode(entry.getKey(), PARAMS_ENCODING));
+ query.append("=");
+ query.append(URLEncoder.encode(entry.getValue(), PARAMS_ENCODING));
+ query.append("&");
+ }
+ } catch (UnsupportedEncodingException uee) {
+ throw new RuntimeException("Encoding not supported: " + PARAMS_ENCODING, uee);
+ }
+ url = String.format("%s?%s", url, query);
+ }
+ return url;
+ }
+
+ //Sets the User-Agent header to be sent with each future request.
+ public void setUserAgent(String userAgent) {
+ mUserAgent = userAgent;
+ }
+
+ // Sets the auth token to be used in the request header
+ public void setAccessToken(String token) {
+ mAccessToken = token;
+ }
+
+ public boolean isAuthenticated() {
+ return mAccessToken != null;
+ }
+}
diff --git a/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestRequest.java b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestRequest.java
new file mode 100644
index 000000000..8ad4653d5
--- /dev/null
+++ b/libs/wpcomrest/WordPressComRest/src/main/java/com/wordpress/rest/RestRequest.java
@@ -0,0 +1,83 @@
+package com.wordpress.rest;
+
+import com.android.volley.NetworkResponse;
+import com.android.volley.ParseError;
+import com.android.volley.Request;
+import com.android.volley.Response;
+import com.android.volley.toolbox.HttpHeaderParser;
+
+import org.json.JSONObject;
+import org.json.JSONException;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.UnsupportedEncodingException;
+
+public class RestRequest extends Request<JSONObject> {
+ public static final String USER_AGENT_HEADER = "User-Agent";
+ public static final String REST_AUTHORIZATION_HEADER = "Authorization";
+ public static final String REST_AUTHORIZATION_FORMAT = "Bearer %s";
+
+ public interface Listener extends Response.Listener<JSONObject> {
+ } //This is just a shortcut for Response.Listener<JSONObject>
+ public interface ErrorListener extends Response.ErrorListener {
+ } //This is just a shortcut for Response.ErrorListener
+
+ private final com.android.volley.Response.Listener<JSONObject> mListener;
+ private final Map<String, String> mParams;
+ private final Map<String, String> mHeaders = new HashMap<String, String>(2);
+
+ public RestRequest(int method, String url, Map<String, String> params,
+ com.android.volley.Response.Listener<JSONObject> listener,
+ com.android.volley.Response.ErrorListener errorListener) {
+ super(method, url, errorListener);
+ mParams = params;
+ mListener = listener;
+ }
+
+ public void removeAccessToken() {
+ setAccessToken(null);
+ }
+
+ public void setAccessToken(String token) {
+ if (token == null) {
+ mHeaders.remove(REST_AUTHORIZATION_HEADER);
+ } else {
+ mHeaders.put(REST_AUTHORIZATION_HEADER, String.format(REST_AUTHORIZATION_FORMAT, token));
+ }
+ }
+
+ public void setUserAgent(String userAgent) {
+ mHeaders.put(USER_AGENT_HEADER, userAgent);
+ }
+
+ @Override
+ public Map<String, String> getHeaders() {
+ return mHeaders;
+ }
+
+ @Override
+ protected void deliverResponse(JSONObject response) {
+ if (mListener != null) {
+ mListener.onResponse(response);
+ }
+ }
+
+ @Override
+ protected Map<String, String> getParams() {
+ return mParams;
+ }
+
+ @Override
+ protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
+ try {
+ String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
+ return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));
+ } catch (UnsupportedEncodingException e) {
+ return Response.error(new ParseError(e));
+ } catch (JSONException je) {
+ return Response.error(new ParseError(je));
+ }
+ }
+}
diff --git a/libs/wpcomrest/build.gradle b/libs/wpcomrest/build.gradle
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/libs/wpcomrest/build.gradle
diff --git a/libs/wpcomrest/gradle/wrapper/gradle-wrapper.jar b/libs/wpcomrest/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..0087cd3b1
--- /dev/null
+++ b/libs/wpcomrest/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/libs/wpcomrest/gradle/wrapper/gradle-wrapper.properties b/libs/wpcomrest/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..55431dfe6
--- /dev/null
+++ b/libs/wpcomrest/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jul 10 14:47:28 CEST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
diff --git a/libs/wpcomrest/gradlew b/libs/wpcomrest/gradlew
new file mode 100755
index 000000000..91a7e269e
--- /dev/null
+++ b/libs/wpcomrest/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/libs/wpcomrest/gradlew.bat b/libs/wpcomrest/gradlew.bat
new file mode 100644
index 000000000..8a0b282aa
--- /dev/null
+++ b/libs/wpcomrest/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/libs/wpcomrest/settings.gradle b/libs/wpcomrest/settings.gradle
new file mode 100644
index 000000000..473ceab34
--- /dev/null
+++ b/libs/wpcomrest/settings.gradle
@@ -0,0 +1 @@
+include ':WordPressComRest'
diff --git a/libs/wpcomrest/test/.gitignore b/libs/wpcomrest/test/.gitignore
new file mode 100644
index 000000000..81e9c938d
--- /dev/null
+++ b/libs/wpcomrest/test/.gitignore
@@ -0,0 +1,3 @@
+bin/*
+local.properties
+gen/* \ No newline at end of file
diff --git a/libs/wpcomrest/test/ant.properties b/libs/wpcomrest/test/ant.properties
new file mode 100644
index 000000000..02634db80
--- /dev/null
+++ b/libs/wpcomrest/test/ant.properties
@@ -0,0 +1,18 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
+tested.project.dir=./app
diff --git a/libs/wpcomrest/test/app/.gitignore b/libs/wpcomrest/test/app/.gitignore
new file mode 100644
index 000000000..81e9c938d
--- /dev/null
+++ b/libs/wpcomrest/test/app/.gitignore
@@ -0,0 +1,3 @@
+bin/*
+local.properties
+gen/* \ No newline at end of file
diff --git a/libs/wpcomrest/test/app/AndroidManifest.xml b/libs/wpcomrest/test/app/AndroidManifest.xml
new file mode 100644
index 000000000..a1ea8b431
--- /dev/null
+++ b/libs/wpcomrest/test/app/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.wordpress.notereader"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name="NotesActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".LoginActivity" />
+ </application>
+ <uses-permission android:name="android.permission.INTERNET" />
+</manifest>
diff --git a/libs/wpcomrest/test/app/ant.properties b/libs/wpcomrest/test/app/ant.properties
new file mode 100644
index 000000000..b0971e891
--- /dev/null
+++ b/libs/wpcomrest/test/app/ant.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/libs/wpcomrest/test/app/build.xml b/libs/wpcomrest/test/app/build.xml
new file mode 100644
index 000000000..b05b8e9e5
--- /dev/null
+++ b/libs/wpcomrest/test/app/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="NotesActivity" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/libs/wpcomrest/test/app/proguard-project.txt b/libs/wpcomrest/test/app/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libs/wpcomrest/test/app/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libs/wpcomrest/test/app/project.properties b/libs/wpcomrest/test/app/project.properties
new file mode 100644
index 000000000..fe8b84557
--- /dev/null
+++ b/libs/wpcomrest/test/app/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-16
+android.library.reference.1=../../
diff --git a/libs/wpcomrest/test/build.xml b/libs/wpcomrest/test/build.xml
new file mode 100644
index 000000000..7f26967c7
--- /dev/null
+++ b/libs/wpcomrest/test/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="NoteReaderTest" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/libs/wpcomrest/test/proguard-project.txt b/libs/wpcomrest/test/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libs/wpcomrest/test/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libs/wpcomrest/test/project.properties b/libs/wpcomrest/test/project.properties
new file mode 100644
index 000000000..9b84a6b4b
--- /dev/null
+++ b/libs/wpcomrest/test/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-16
diff --git a/libs/wpcomrest/tools/deploy-mvn-artifact.conf-example b/libs/wpcomrest/tools/deploy-mvn-artifact.conf-example
new file mode 100644
index 000000000..5aaf644ad
--- /dev/null
+++ b/libs/wpcomrest/tools/deploy-mvn-artifact.conf-example
@@ -0,0 +1 @@
+LOCAL_GH_PAGES=file:///Users/max/work/automattic/WordPress-Android-gh-pages/
diff --git a/libs/wpcomrest/tools/deploy-mvn-artifact.sh b/libs/wpcomrest/tools/deploy-mvn-artifact.sh
new file mode 100755
index 000000000..e76998f93
--- /dev/null
+++ b/libs/wpcomrest/tools/deploy-mvn-artifact.sh
@@ -0,0 +1,19 @@
+#!/bin/sh +v
+
+. tools/deploy-mvn-artifact.conf
+PROJECT=WordPressComRest
+VERSION=`grep -E 'versionName' $PROJECT/build.gradle \
+ | sed s/versionName// \
+ | grep -Eo "[a-zA-Z0-9.-]+"`
+GROUPID=com.automattic
+ARTIFACTID=wordpresscom-rest
+AARFILE=$PROJECT/build/outputs/aar/WordPressComRest.aar
+
+# Deploy release build
+mvn deploy:deploy-file -Dfile=$AARFILE \
+ -Durl=$LOCAL_GH_PAGES -DgroupId=$GROUPID \
+ -DartifactId=$ARTIFACTID -Dversion=$VERSION
+
+echo ========================================
+echo
+echo \"$GROUPID:$ARTIFACTID:$VERSION\" deployed \ No newline at end of file