├── app
├── .gitignore
├── src
│ └── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_egg.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_egg.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_egg.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_egg.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_egg.png
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── styles.xml
│ │ │ ├── strings.xml
│ │ │ └── colors.xml
│ │ ├── drawable
│ │ │ ├── ic_add_white_24dp.xml
│ │ │ └── ic_send_black_24dp.xml
│ │ ├── menu
│ │ │ └── post_list_menu.xml
│ │ ├── layout
│ │ │ ├── fragment_post_list.xml
│ │ │ ├── activity_post_list.xml
│ │ │ ├── fragment_url_check.xml
│ │ │ ├── fragment_two_factor_auth.xml
│ │ │ ├── activity_login.xml
│ │ │ ├── fragment_email_password.xml
│ │ │ ├── new_post.xml
│ │ │ └── post_item.xml
│ │ └── anim
│ │ │ └── pressed_card.xml
│ │ ├── java
│ │ └── org
│ │ │ └── wordpress
│ │ │ └── pioupiou
│ │ │ ├── misc
│ │ │ ├── AppSecretsModule.java
│ │ │ ├── PioupiouApp.java
│ │ │ └── AppComponent.java
│ │ │ ├── login
│ │ │ ├── URLCheckFragment.java
│ │ │ ├── EmailPasswordFragment.java
│ │ │ ├── TwoFactorAuthFragment.java
│ │ │ └── LoginActivity.java
│ │ │ └── postlist
│ │ │ ├── PostListFragment.java
│ │ │ ├── PostRecyclerViewAdapter.java
│ │ │ └── PostListActivity.java
│ │ └── AndroidManifest.xml
├── gradle.properties-example
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── assets
├── launcher_icon.afdesign
└── launcher_icon.svg
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── .travis.yml
├── README.md
├── gradlew.bat
├── gradlew
└── config
└── checkstyle.xml
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/launcher_icon.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/assets/launcher_icon.afdesign
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-hdpi/ic_egg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-mdpi/ic_egg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xhdpi/ic_egg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xxhdpi/ic_egg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xxxhdpi/ic_egg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/Pioupiou/trunk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/gradle.properties-example:
--------------------------------------------------------------------------------
1 | wp.OAUTH.APP.ID = wp
2 | wp.OAUTH.APP.SECRET = wp
3 | wp.SITE_DOMAIN = [the domain of the site to use when displaying posts, ex: ponyexpress.wordpress.com]
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea/
11 | app/gradle.properties
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jan 13 09:01:16 CET 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 8dp
7 | 48dp
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_send_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/post_list_menu.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/misc/AppSecretsModule.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.misc;
2 |
3 | import org.wordpress.android.fluxc.network.rest.wpcom.auth.AppSecrets;
4 | import org.wordpress.pioupiou.BuildConfig;
5 |
6 | import dagger.Module;
7 | import dagger.Provides;
8 |
9 | @Module
10 | public class AppSecretsModule {
11 | @Provides
12 | public AppSecrets provideAppSecrets() {
13 | return new AppSecrets(BuildConfig.OAUTH_APP_ID, BuildConfig.OAUTH_APP_SECRET);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_post_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/login/URLCheckFragment.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.login;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import org.wordpress.pioupiou.R;
10 |
11 | public class URLCheckFragment extends Fragment {
12 | @Override
13 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
14 | Bundle savedInstanceState) {
15 | View view = inflater.inflate(R.layout.fragment_url_check, container, false);
16 | ((LoginActivity) getActivity()).bindUrlFragmentReferences(view);
17 | return view;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/login/EmailPasswordFragment.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.login;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import org.wordpress.pioupiou.R;
10 |
11 | public class EmailPasswordFragment extends Fragment {
12 | @Override
13 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
14 | Bundle savedInstanceState) {
15 | View view = inflater.inflate(R.layout.fragment_email_password, container, false);
16 | ((LoginActivity) getActivity()).bindEmailPasswordFragmentReferences(view);
17 | return view;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/login/TwoFactorAuthFragment.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.login;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import org.wordpress.pioupiou.R;
10 |
11 | public class TwoFactorAuthFragment extends Fragment {
12 | @Override
13 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
14 | Bundle savedInstanceState) {
15 | View view = inflater.inflate(R.layout.fragment_two_factor_auth, container, false);
16 | ((LoginActivity) getActivity()).bindTwoFactorAuthFragmentReferences(view);
17 | return view;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/pressed_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
11 |
12 | -
13 |
18 |
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk8
3 |
4 | android:
5 | components:
6 | - extra-android-m2repository
7 | - extra-android-support
8 | - platform-tools
9 | - tools
10 | - build-tools-25.0.3
11 | - android-25
12 |
13 | env:
14 | global:
15 | - GRADLE_OPTS="-XX:MaxPermSize=4g -Xmx4g"
16 | - ANDROID_SDKS=android-14
17 | - ANDROID_TARGET=android-14
18 |
19 | before_install:
20 | # TODO: Remove the following line when Travis' platform-tools are updated to v24+
21 | - echo yes | android update sdk -a --filter platform-tools --no-ui --force
22 |
23 | install:
24 | # Setup gradle.properties
25 | - cp app/gradle.properties-example app/gradle.properties
26 |
27 | script:
28 | - ./gradlew assembleDebug assembleRelease
29 | - ./gradlew lint || (grep -A20 -B2 'severity="Error"' */build/outputs/*.xml; exit 1)
30 | - ./gradlew checkstyle
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/max/work/android-sdk-mac/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/misc/PioupiouApp.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.misc;
2 |
3 | import android.app.Application;
4 |
5 | import com.yarolegovich.wellsql.WellSql;
6 |
7 | import org.wordpress.android.fluxc.module.AppContextModule;
8 | import org.wordpress.android.fluxc.persistence.WellSqlConfig;
9 |
10 | import timber.log.Timber;
11 |
12 | public class PioupiouApp extends Application {
13 | private AppComponent mComponent;
14 |
15 | @Override
16 | public void onCreate() {
17 | super.onCreate();
18 | // Init Dagger
19 | mComponent = DaggerAppComponent.builder()
20 | .appContextModule(new AppContextModule(getApplicationContext()))
21 | .build();
22 | component().inject(this);
23 |
24 | // Init WellSql
25 | WellSql.init(new WellSqlConfig(getApplicationContext()));
26 |
27 | // Init Timber
28 | Timber.plant(new Timber.DebugTree());
29 | }
30 |
31 | public AppComponent component() {
32 | return mComponent;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/misc/AppComponent.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.misc;
2 |
3 | import org.wordpress.android.fluxc.module.AppContextModule;
4 | import org.wordpress.android.fluxc.module.ReleaseBaseModule;
5 | import org.wordpress.android.fluxc.module.ReleaseNetworkModule;
6 | import org.wordpress.android.fluxc.module.ReleaseOkHttpClientModule;
7 | import org.wordpress.android.fluxc.module.ReleaseStoreModule;
8 | import org.wordpress.pioupiou.login.LoginActivity;
9 | import org.wordpress.pioupiou.postlist.PostListActivity;
10 |
11 | import javax.inject.Singleton;
12 |
13 | import dagger.Component;
14 |
15 | @Singleton
16 | @Component(modules = {
17 | AppContextModule.class,
18 | AppSecretsModule.class,
19 | ReleaseOkHttpClientModule.class,
20 | ReleaseBaseModule.class,
21 | ReleaseNetworkModule.class,
22 | ReleaseOkHttpClientModule.class,
23 | ReleaseStoreModule.class
24 | })
25 | public interface AppComponent {
26 | void inject(PioupiouApp object);
27 | void inject(LoginActivity object);
28 | void inject(PostListActivity object);
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_post_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
21 |
22 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Pioupiou
3 |
4 |
5 |
6 | Pioupiou
7 | What\'s your site address (also called site URL)? It should be something like:\n - ponyexpress.com\n - ponyexpress.wordpress.com\n - http://www.ponyexpre.ss
8 | Enter your username (or email address) and password
9 | Enter auth code
10 | Site address (URL)
11 | Username or email
12 | Password
13 | 2fa code
14 | Next
15 | Log in
16 | This site address (URL) is invalid
17 | This email address is invalid
18 | This password is too short
19 | This password is incorrect
20 | This field is required
21 |
22 |
23 | \u00A0–\u00A0
24 | Create new micro post
25 |
26 |
27 | What\'s on your mind?
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_url_check.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
33 |
34 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_two_factor_auth.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
35 |
36 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "org.wordpress.pioupiou"
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | vectorDrawables.useSupportLibrary = true
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | lintOptions {
22 | disable 'InvalidPackage'
23 | }
24 | }
25 |
26 | // Add properties named "wp.xxx" to our BuildConfig
27 | android.buildTypes.all { buildType ->
28 | project.properties.any { property ->
29 | if (property.key.toLowerCase().startsWith("wp.")) {
30 | buildType.buildConfigField "String", property.key.replace("wp.", "").replace(".", "_").toUpperCase(),
31 | "\"${property.value}\""
32 | }
33 | }
34 | }
35 |
36 | dependencies {
37 | compile ('com.github.wordpress-mobile.WordPress-FluxC-Android:fluxc:d91145d2206817c87e67a473304085a7ef3feb79') {
38 | exclude group : 'com.android.support'
39 | }
40 | compile 'com.android.support:appcompat-v7:25.3.1'
41 | compile 'com.android.support:design:25.3.1'
42 | compile 'com.android.support:support-v4:25.3.1'
43 | compile 'com.android.support:recyclerview-v7:25.3.1'
44 | compile 'com.android.support:cardview-v7:25.3.1'
45 | compile 'com.google.dagger:dagger:2.0.2'
46 | compile 'com.squareup.picasso:picasso:2.5.2'
47 | compile 'com.jakewharton.timber:timber:3.1.0'
48 | compile 'org.wordpress:persistentedittext:1.0.2'
49 | annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
50 | provided 'org.glassfish:javax.annotation:10.0-b28'
51 | compile 'com.fenchtose:tooltip:0.1.5'
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
17 |
18 |
25 |
26 |
32 |
33 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Pioupiou
4 |
5 | Micro blogging app using your WordPress site as backend.
6 |
7 | ## Build Instructions ##
8 |
9 | You first need to create the `gradle.properties` file:
10 |
11 | ```shell
12 | $ cp app/gradle.properties-example app/gradle.properties
13 | ```
14 |
15 | Note: this is the default `./gradle.properties` file. If you
16 | want to use a WordPress.com account, you'll have to get a WordPress.com
17 | OAuth2 ID and secret. Please read the
18 | [OAuth2 Authentication](#oauth2-authentication) section.
19 |
20 | For WordPress.com accounts, you should also fill in `wp.SITE_DOMAIN`
21 | with the site in your account you wish to use with PiouPiou. If this
22 | is left blank, the default site will be used.
23 |
24 | You can now build the project:
25 |
26 | ```shell
27 | $ ./gradlew build
28 | ```
29 |
30 | You can use [Android Studio][1] by importing the project as a Gradle project.
31 |
32 | ## OAuth2 Authentication ##
33 |
34 | In order to use WordPress.com functions you will need a client ID and
35 | a client secret key. These details will be used to authenticate your
36 | application and verify that the API calls being made are valid. You can
37 | create an application or view details for your existing applications with
38 | our [WordPress.com applications manager][2].
39 |
40 | When creating your application, you should select "Native client" for the
41 | application type. The applications manager currently requires a "redirect URL",
42 | but this isn't used for mobile apps. Just use "https://localhost".
43 |
44 | Once you've created your application in the [applications manager][2], you'll
45 | need to edit the `./gradle.properties` file and change the
46 | `WP.OAUTH.APP.ID` and `WP.OAUTH.APP.SECRET` fields. Then you can compile and
47 | run the app on a device or an emulator and try to login with a WordPress.com
48 | account.
49 |
50 | Read more about [OAuth2][3] and the [WordPress.com REST endpoint][4].
51 |
52 | [1]: http://developer.android.com/sdk/installing/studio.html
53 | [2]: https://developer.wordpress.com/apps/
54 | [3]: https://developer.wordpress.com/docs/oauth2/
55 | [4]: https://developer.wordpress.com/docs/api/
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_email_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
32 |
33 |
46 |
47 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/assets/launcher_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/blue_wordpress
4 | @color/blue_dark
5 | @color/orange_jazzy
6 |
7 |
8 |
9 |
10 |
11 | #0087be
12 | #78dcfa
13 | #00aadc
14 | #005082
15 |
16 |
17 | #87a6bc
18 | #f3f6f8
19 | #2e4453
20 | @color/grey_darken_10
21 |
22 |
23 | #e9eff3
24 | #c8d7e1
25 | #a8bece
26 | #668eaa
27 | #4f748e
28 | #3d596d
29 |
30 |
31 | #f0821e
32 | #d54e21
33 |
34 |
35 | #f0b849
36 | #d94f4f
37 | #4ab866
38 |
39 |
40 | #B32e4453
41 | #cc78dcfa
42 | #802e4453
43 | #80a8bece
44 | #80c8d7e1
45 | #80e9eff3
46 | #80888888
47 |
48 |
49 |
50 |
51 |
52 |
53 | @color/grey_lighten_20
54 | @color/grey_dark
55 | @color/blue_wordpress
56 | @color/grey_lighten_20
57 | @color/grey_lighten_10
58 |
59 |
60 | #80000000
61 | #00000000
62 | @color/grey_lighten_30
63 | @color/grey_lighten_20
64 | #FFFFFF
65 | #F1F1F1
66 |
67 | #000000
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/new_post.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
16 |
17 |
22 |
23 |
32 |
33 |
49 |
50 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/post_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
23 |
29 |
30 |
36 |
37 |
41 |
42 |
48 |
49 |
55 |
56 |
62 |
63 |
64 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/postlist/PostListFragment.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.postlist;
2 |
3 | import android.app.Fragment;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.v7.widget.GridLayoutManager;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import org.wordpress.android.fluxc.model.AccountModel;
15 | import org.wordpress.android.fluxc.model.PostModel;
16 | import org.wordpress.pioupiou.R;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * A fragment representing a list of Items.
22 | *
23 | * Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener}
24 | * interface.
25 | */
26 | public class PostListFragment extends Fragment {
27 | private static final String ARG_COLUMN_COUNT = "column-count";
28 | private int mColumnCount = 1;
29 | private OnListFragmentInteractionListener mListener;
30 | private RecyclerView mRecyclerView;
31 | private PostRecyclerViewAdapter mAdapter;
32 |
33 | /**
34 | * Mandatory empty constructor for the fragment manager to instantiate the
35 | * fragment (e.g. upon screen orientation changes).
36 | */
37 | public PostListFragment() {
38 | }
39 |
40 | @SuppressWarnings("unused")
41 | public static PostListFragment newInstance(int columnCount) {
42 | PostListFragment fragment = new PostListFragment();
43 | Bundle args = new Bundle();
44 | args.putInt(ARG_COLUMN_COUNT, columnCount);
45 | fragment.setArguments(args);
46 | return fragment;
47 | }
48 |
49 | @Override
50 | public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 |
53 | if (getArguments() != null) {
54 | mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
55 | }
56 | }
57 |
58 | @Override
59 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
60 | Bundle savedInstanceState) {
61 | View view = inflater.inflate(R.layout.fragment_post_list, container, false);
62 | mRecyclerView = (RecyclerView) view.findViewById(R.id.list);
63 | if (mColumnCount <= 1) {
64 | mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
65 | } else {
66 | mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), mColumnCount));
67 | }
68 |
69 | mAdapter = new PostRecyclerViewAdapter(getActivity(), mListener);
70 | mRecyclerView.setAdapter(mAdapter);
71 |
72 | return view;
73 | }
74 |
75 | void setPosts(@NonNull AccountModel account, @NonNull List posts) {
76 | mAdapter.setPosts(account, posts);
77 | }
78 |
79 | @Override
80 | public void onAttach(Context context) {
81 | super.onAttach(context);
82 | if (context instanceof OnListFragmentInteractionListener) {
83 | mListener = (OnListFragmentInteractionListener) context;
84 | } else {
85 | throw new RuntimeException(context.toString()
86 | + " must implement OnListFragmentInteractionListener");
87 | }
88 | }
89 |
90 | @Override
91 | public void onDetach() {
92 | super.onDetach();
93 | mListener = null;
94 | }
95 |
96 | public interface OnListFragmentInteractionListener {
97 | void onListFragmentInteraction(PostModel item);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/postlist/PostRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.postlist;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.support.annotation.NonNull;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.text.TextUtils;
8 | import android.text.format.DateUtils;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.ImageView;
13 | import android.widget.TextView;
14 |
15 | import com.squareup.picasso.Picasso;
16 | import com.squareup.picasso.Transformation;
17 |
18 | import org.wordpress.android.fluxc.model.AccountModel;
19 | import org.wordpress.android.fluxc.model.PostModel;
20 | import org.wordpress.android.util.DateTimeUtils;
21 | import org.wordpress.android.util.HtmlUtils;
22 | import org.wordpress.android.util.ImageUtils;
23 | import org.wordpress.pioupiou.R;
24 | import org.wordpress.pioupiou.postlist.PostListFragment.OnListFragmentInteractionListener;
25 |
26 | import java.text.BreakIterator;
27 | import java.util.ArrayList;
28 | import java.util.Date;
29 | import java.util.List;
30 |
31 | public class PostRecyclerViewAdapter extends RecyclerView.Adapter {
32 | private final OnListFragmentInteractionListener mListener;
33 | private final LayoutInflater mInflater;
34 | private final int mAvatarSz;
35 | private AccountModel mAccount;
36 | private List mPosts = new ArrayList<>();
37 |
38 | public PostRecyclerViewAdapter(Context context,
39 | OnListFragmentInteractionListener listener) {
40 | mInflater = LayoutInflater.from(context);
41 | mListener = listener;
42 | mAvatarSz = context.getResources().getDimensionPixelSize(R.dimen.post_avatar);
43 | }
44 |
45 | public void setPosts(@NonNull AccountModel account, @NonNull List posts) {
46 | mAccount = account;
47 | mPosts = posts;
48 | notifyDataSetChanged();
49 | }
50 |
51 | @Override
52 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
53 | View view = mInflater.inflate(R.layout.post_item, parent, false);
54 | return new ViewHolder(view);
55 | }
56 |
57 | @Override
58 | public void onBindViewHolder(final ViewHolder holder, int position) {
59 | PostModel post = mPosts.get(position);
60 |
61 | // TODO: we get the author from the account but it should be part of the post model
62 | holder.mAuthorView.setText(mAccount.getDisplayName());
63 | holder.mContentView.setText(makeExcerpt(post.getContent()));
64 |
65 | if (!post.getDateCreated().isEmpty()) {
66 | Date date = DateTimeUtils.dateFromIso8601(post.getDateCreated());
67 | holder.mDateView.setText(DateUtils.getRelativeTimeSpanString(
68 | date.getTime(),
69 | System.currentTimeMillis(),
70 | DateUtils.SECOND_IN_MILLIS,
71 | DateUtils.FORMAT_ABBREV_ALL));
72 | } else {
73 | holder.mDateView.setText("unknown");
74 | }
75 |
76 | Picasso.with(holder.itemView.getContext())
77 | .load(mAccount.getAvatarUrl())
78 | .placeholder(R.mipmap.ic_egg)
79 | .transform(mTransformation)
80 | .resize(mAvatarSz, mAvatarSz)
81 | .into(holder.mAvatarView);
82 | }
83 |
84 | private final Transformation mTransformation = new Transformation() {
85 | @Override
86 | public Bitmap transform(Bitmap source) {
87 | Bitmap circular = ImageUtils.getCircularBitmap(source);
88 | source.recycle();
89 | return circular;
90 | }
91 | @Override
92 | public String key() {
93 | return "circular-avatar";
94 | }
95 | };
96 |
97 | @Override
98 | public int getItemCount() {
99 | return mPosts.size();
100 | }
101 |
102 | /*
103 | * return an excerpt from the full post content - breaks at words rather than at a specific
104 | * character position
105 | */
106 | private static final int MAX_EXCERPT_LEN = 200;
107 | private static String makeExcerpt(final String content) {
108 | if (TextUtils.isEmpty(content)) {
109 | return null;
110 | }
111 |
112 | String text = HtmlUtils.fastStripHtml(content);
113 | if (text.length() <= MAX_EXCERPT_LEN) {
114 | return text.trim();
115 | }
116 |
117 | StringBuilder result = new StringBuilder();
118 | BreakIterator wordIterator = BreakIterator.getWordInstance();
119 | wordIterator.setText(text);
120 | int start = wordIterator.first();
121 | int end = wordIterator.next();
122 | int totalLen = 0;
123 | while (end != BreakIterator.DONE) {
124 | String word = text.substring(start, end);
125 | result.append(word);
126 | totalLen += word.length();
127 | if (totalLen >= MAX_EXCERPT_LEN) {
128 | break;
129 | }
130 | start = end;
131 | end = wordIterator.next();
132 | }
133 |
134 | if (totalLen == 0) {
135 | return null;
136 | }
137 |
138 | return result.toString().trim() + "…";
139 | }
140 |
141 | public class ViewHolder extends RecyclerView.ViewHolder {
142 | private final ImageView mAvatarView;
143 | private final TextView mAuthorView;
144 | private final TextView mContentView;
145 | private final TextView mDateView;
146 |
147 | public ViewHolder(View view) {
148 | super(view);
149 |
150 | mAuthorView = (TextView) view.findViewById(R.id.author);
151 | mContentView = (TextView) view.findViewById(R.id.message);
152 | mAvatarView = (ImageView) view.findViewById(R.id.gravatar_view);
153 | mDateView = (TextView) view.findViewById(R.id.date);
154 |
155 | view.setOnClickListener(new View.OnClickListener() {
156 | @Override
157 | public void onClick(View v) {
158 | if (mListener != null) {
159 | int position = getAdapterPosition();
160 | PostModel post = mPosts.get(position);
161 | mListener.onListFragmentInteraction(post);
162 | }
163 | }
164 | });
165 | }
166 |
167 | @Override
168 | public String toString() {
169 | return super.toString() + " '" + mContentView.getText() + "'";
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/config/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/postlist/PostListActivity.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.postlist;
2 |
3 | import android.app.Fragment;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.v4.content.ContextCompat;
8 | import android.support.v4.widget.SwipeRefreshLayout;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.text.TextUtils;
11 | import android.view.Menu;
12 | import android.view.MenuInflater;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.view.ViewGroup;
17 | import android.view.inputmethod.InputMethodManager;
18 | import android.widget.ImageView;
19 |
20 | import com.fenchtose.tooltip.Tooltip;
21 | import com.fenchtose.tooltip.Tooltip.Listener;
22 |
23 | import org.greenrobot.eventbus.Subscribe;
24 | import org.greenrobot.eventbus.ThreadMode;
25 | import org.wordpress.android.fluxc.Dispatcher;
26 | import org.wordpress.android.fluxc.action.PostAction;
27 | import org.wordpress.android.fluxc.generated.PostActionBuilder;
28 | import org.wordpress.android.fluxc.model.PostModel;
29 | import org.wordpress.android.fluxc.model.SiteModel;
30 | import org.wordpress.android.fluxc.store.AccountStore;
31 | import org.wordpress.android.fluxc.store.PostStore;
32 | import org.wordpress.android.fluxc.store.PostStore.OnPostChanged;
33 | import org.wordpress.android.fluxc.store.PostStore.OnPostUploaded;
34 | import org.wordpress.android.fluxc.store.PostStore.RemotePostPayload;
35 | import org.wordpress.android.fluxc.store.SiteStore;
36 | import org.wordpress.android.util.ToastUtils;
37 | import org.wordpress.android.util.UrlUtils;
38 | import org.wordpress.persistentedittext.PersistentEditText;
39 | import org.wordpress.pioupiou.BuildConfig;
40 | import org.wordpress.pioupiou.R;
41 | import org.wordpress.pioupiou.misc.PioupiouApp;
42 | import org.wordpress.pioupiou.postlist.PostListFragment.OnListFragmentInteractionListener;
43 |
44 | import java.util.List;
45 |
46 | import javax.inject.Inject;
47 |
48 | import timber.log.Timber;
49 |
50 | public class PostListActivity extends AppCompatActivity implements OnListFragmentInteractionListener {
51 | // UI references
52 | private SwipeRefreshLayout mSwipeRefreshLayout;
53 |
54 | // State
55 | private boolean mNewPostVisible;
56 | private boolean mIsFetchingPosts;
57 |
58 | private String mNewPostContent;
59 |
60 | // FluxC
61 | @Inject Dispatcher mDispatcher;
62 | @Inject PostStore mPostStore;
63 | @Inject SiteStore mSiteStore;
64 | @Inject AccountStore mAccountStore;
65 |
66 | @Override
67 | protected void onCreate(Bundle savedInstanceState) {
68 | super.onCreate(savedInstanceState);
69 | ((PioupiouApp) getApplication()).component().inject(this);
70 |
71 | setContentView(R.layout.activity_post_list);
72 |
73 | mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_to_refresh);
74 | mSwipeRefreshLayout.setOnRefreshListener(
75 | new SwipeRefreshLayout.OnRefreshListener() {
76 | @Override
77 | public void onRefresh() {
78 | if (!mIsFetchingPosts) {
79 | fetchPosts(getSite());
80 | }
81 | }
82 | }
83 | );
84 |
85 | // immediately show existing posts then fetch the latest from the server
86 | SiteModel site = getSite();
87 | if (site == null) {
88 | Timber.w("Can't show posts for null site");
89 | showError("No site found");
90 | } else {
91 | showPosts(site);
92 | fetchPosts(site);
93 | }
94 | }
95 |
96 | @Override
97 | public void onStart() {
98 | super.onStart();
99 | mDispatcher.register(this);
100 | }
101 |
102 | @Override
103 | public void onStop() {
104 | super.onStop();
105 | mDispatcher.unregister(this);
106 | }
107 |
108 | private void showPosts(@NonNull SiteModel site) {
109 | Timber.i("Show posts started");
110 | if (hasPostListFragment()) {
111 | getPostListFragment().setPosts(mAccountStore.getAccount(), mPostStore.getPostsForSite(site));
112 | }
113 | }
114 |
115 | private void fetchPosts(@NonNull SiteModel site) {
116 | Timber.i("Fetch posts started");
117 | mIsFetchingPosts = true;
118 | mDispatcher.dispatch(PostActionBuilder.newFetchPostsAction(
119 | new PostStore.FetchPostsPayload(site)));
120 | mSwipeRefreshLayout.setRefreshing(true);
121 | }
122 |
123 | /*
124 | * returns the site to use for the post list - relies on wp.SITE_DOMAIN in app/build.gradle
125 | */
126 | private SiteModel getSite() {
127 | List sites = mSiteStore.getSitesByNameOrUrlMatching(UrlUtils.removeScheme(BuildConfig.SITE_DOMAIN));
128 | if (sites.size() != 0) {
129 | return sites.get(0);
130 | } else {
131 | return null;
132 | }
133 | }
134 |
135 | /*
136 | * called when the user taps a post in the list
137 | */
138 | @Override
139 | public void onListFragmentInteraction(PostModel post) {
140 | Timber.i("Post tapped");
141 | // uncomment to delete a post when tapped - useful to remove test posts
142 | // PostStore.RemotePostPayload payload = new PostStore.RemotePostPayload(post, getSite());
143 | // mDispatcher.dispatch(PostActionBuilder.newDeletePostAction(payload));
144 | // showProgress(true);
145 | }
146 |
147 | // Menu
148 |
149 | @Override
150 | public boolean onCreateOptionsMenu(Menu menu) {
151 | MenuInflater inflater = getMenuInflater();
152 | inflater.inflate(R.menu.post_list_menu, menu);
153 | return true;
154 | }
155 |
156 | @Override
157 | public boolean onOptionsItemSelected(MenuItem item) {
158 | switch (item.getItemId()) {
159 | case R.id.action_add:
160 | Timber.i("New post");
161 | createNewPost();
162 | // TODO: show the "New message" UI
163 | return true;
164 | default:
165 | return super.onOptionsItemSelected(item);
166 | }
167 | }
168 |
169 | private void createNewPost() {
170 | if (mNewPostVisible) {
171 | return;
172 | }
173 | mNewPostVisible = true;
174 |
175 | View anchor = findViewById(R.id.action_add);
176 | View newPostView = getLayoutInflater().inflate(R.layout.new_post, null);
177 | final PersistentEditText editText = (PersistentEditText) newPostView.findViewById(R.id.new_post_edit_text);
178 | ImageView sendButton = (ImageView) newPostView.findViewById(R.id.send_post);
179 | int tipSizeSmall = getResources().getDimensionPixelSize(R.dimen.text_margin);
180 | int tooltipColor = ContextCompat.getColor(this, R.color.nux_background);
181 |
182 | final Tooltip tooltip = new Tooltip.Builder(this)
183 | .anchor(anchor, Tooltip.BOTTOM)
184 | // Can't use animation because the tooltip library starts the animation when onLayout() is called
185 | // .animate(new TooltipAnimation(TooltipAnimation.REVEAL, 400))
186 | .autoAdjust(true)
187 | .content(newPostView)
188 | .cancelable(true)
189 | .withPadding(getResources().getDimensionPixelOffset(R.dimen.tooltip_padding))
190 | .withTip(new Tooltip.Tip(tipSizeSmall, tipSizeSmall / 2, tooltipColor))
191 | .into((ViewGroup) findViewById(R.id.root))
192 | .debug(true)
193 | .withListener(new Listener() {
194 | @Override
195 | public void onDismissed() {
196 | mNewPostVisible = false;
197 | // Hide keyboard
198 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
199 | imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
200 | }
201 | })
202 | .show();
203 |
204 | sendButton.setOnClickListener(new OnClickListener() {
205 | @Override
206 | public void onClick(View v) {
207 | mNewPostContent = editText.getText().toString();
208 | // Clear the saved text
209 | editText.setText("");
210 | tooltip.dismiss(true);
211 | publishPost();
212 | }
213 | });
214 | }
215 |
216 | // Publish post
217 | private void publishPost() {
218 | if (TextUtils.isEmpty(mNewPostContent)) {
219 | ToastUtils.showToast(this, "Can't publish an empty post");
220 | return;
221 | }
222 |
223 | // instantiate a new post with the content and publish it
224 | showProgress(true);
225 | PostModel post = mPostStore.instantiatePostModel(getSite(), false);
226 | post.setContent(mNewPostContent);
227 | RemotePostPayload payload = new RemotePostPayload(post, getSite());
228 | mDispatcher.dispatch(PostActionBuilder.newPushPostAction(payload));
229 | }
230 |
231 | private boolean hasPostListFragment() {
232 | return getPostListFragment() != null;
233 | }
234 |
235 | private PostListFragment getPostListFragment() {
236 | Fragment fragment = getFragmentManager().findFragmentById(R.id.list);
237 | if (fragment instanceof PostListFragment) {
238 | return (PostListFragment) fragment;
239 | } else {
240 | return null;
241 | }
242 | }
243 |
244 | private void showProgress(boolean show) {
245 | View progress = findViewById(R.id.progress);
246 | progress.setVisibility(show ? View.VISIBLE : View.GONE);
247 | }
248 |
249 | private void showError(String message) {
250 | ToastUtils.showToast(this, message, ToastUtils.Duration.LONG);
251 | }
252 |
253 | /*
254 | * called whenever the post list changes, such as when a fetch posts has completed
255 | */
256 | @SuppressWarnings("unused")
257 | @Subscribe(threadMode = ThreadMode.MAIN)
258 | public void onPostChanged(OnPostChanged event) {
259 | if (event.causeOfChange == PostAction.FETCH_POSTS) {
260 | mIsFetchingPosts = false;
261 | }
262 |
263 | if (!event.isError()) {
264 | showPosts(getSite());
265 | } else {
266 | showError("OnPostChanged error - "
267 | + event.error.message
268 | + " (" + event.causeOfChange.toString() + ")");
269 | }
270 |
271 | mSwipeRefreshLayout.setRefreshing(false);
272 | }
273 |
274 | /*
275 | * called when a new post has been uploaded
276 | */
277 | @SuppressWarnings("unused")
278 | @Subscribe(threadMode = ThreadMode.MAIN)
279 | public void onPostUploaded(OnPostUploaded event) {
280 | showProgress(false);
281 | if (!event.isError()) {
282 | showPosts(getSite());
283 | } else {
284 | showError("OnPostUploaded error - " + event.error.message);
285 | }
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/app/src/main/java/org/wordpress/pioupiou/login/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package org.wordpress.pioupiou.login;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.text.TextUtils;
7 | import android.util.Patterns;
8 | import android.view.KeyEvent;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 | import android.view.animation.LinearInterpolator;
12 | import android.view.inputmethod.EditorInfo;
13 | import android.widget.Button;
14 | import android.widget.EditText;
15 | import android.widget.TextView;
16 | import android.widget.Toast;
17 |
18 | import org.greenrobot.eventbus.Subscribe;
19 | import org.greenrobot.eventbus.ThreadMode;
20 | import org.wordpress.android.fluxc.Dispatcher;
21 | import org.wordpress.android.fluxc.generated.AccountActionBuilder;
22 | import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder;
23 | import org.wordpress.android.fluxc.generated.SiteActionBuilder;
24 | import org.wordpress.android.fluxc.store.AccountStore;
25 | import org.wordpress.android.fluxc.store.AccountStore.AuthenticatePayload;
26 | import org.wordpress.android.fluxc.store.AccountStore.AuthenticationErrorType;
27 | import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged;
28 | import org.wordpress.android.fluxc.store.AccountStore.OnDiscoveryResponse;
29 | import org.wordpress.android.fluxc.store.SiteStore;
30 | import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged;
31 | import org.wordpress.android.fluxc.store.SiteStore.OnURLChecked;
32 | import org.wordpress.android.fluxc.store.SiteStore.RefreshSitesXMLRPCPayload;
33 | import org.wordpress.pioupiou.R;
34 | import org.wordpress.pioupiou.misc.PioupiouApp;
35 | import org.wordpress.pioupiou.postlist.PostListActivity;
36 |
37 | import javax.inject.Inject;
38 |
39 | import timber.log.Timber;
40 |
41 | public class LoginActivity extends Activity {
42 | // UI references
43 | private TextView mHelpView;
44 | private EditText mUrlView;
45 | private EditText mEmailView;
46 | private EditText mPasswordView;
47 | private EditText mAuthCodeView;
48 | private Button mNextButton;
49 | private Button mLogInButton;
50 | private Button mLogInWithCodeButton;
51 | private View mImageEggView;
52 |
53 | // State
54 | private boolean mUrlValidated;
55 | private boolean mUrlIsWPCom;
56 | private String mUrl;
57 | private String mXMLRPCUrl;
58 |
59 | // FluxC
60 | @Inject Dispatcher mDispatcher;
61 | @Inject AccountStore mAccountStore;
62 | @Inject SiteStore mSiteStore;
63 |
64 | @Override
65 | protected void onCreate(Bundle savedInstanceState) {
66 | super.onCreate(savedInstanceState);
67 | // Injection
68 | ((PioupiouApp) getApplication()).component().inject(this);
69 |
70 | // If the user has an access token or a self hosted site, we consider they're logged in.
71 | if (mAccountStore.hasAccessToken() || mSiteStore.hasSiteAccessedViaXMLRPC()) {
72 | showPostListAndFinish();
73 | }
74 |
75 | // Init the layout and UI references
76 | setContentView(R.layout.activity_login);
77 | mHelpView = (TextView) findViewById(R.id.login_help);
78 | mImageEggView = findViewById(R.id.image_egg);
79 | getFragmentManager().beginTransaction().add(R.id.fragment_container, new URLCheckFragment()).commit();
80 | }
81 |
82 | public void bindUrlFragmentReferences(View view) {
83 | mUrlView = (EditText) view.findViewById(R.id.url);
84 | mUrlView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
85 | @Override
86 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
87 | if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
88 | return false;
89 | }
90 |
91 | if (id == R.id.checkUrl || id == EditorInfo.IME_NULL) {
92 | checkURLField();
93 | return true;
94 | }
95 | return false;
96 | }
97 | });
98 |
99 | mNextButton = (Button) view.findViewById(R.id.next_button);
100 | mNextButton.setOnClickListener(new OnClickListener() {
101 | @Override
102 | public void onClick(View view) {
103 | checkURLField();
104 | }
105 | });
106 | }
107 |
108 | public void bindEmailPasswordFragmentReferences(View view) {
109 | mEmailView = (EditText) view.findViewById(R.id.email);
110 | mPasswordView = (EditText) view.findViewById(R.id.password);
111 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
112 | @Override
113 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
114 | if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
115 | return false;
116 | }
117 |
118 | if (id == R.id.login || id == EditorInfo.IME_NULL) {
119 | attemptLogin();
120 | return true;
121 | }
122 | return false;
123 | }
124 | });
125 |
126 | mLogInButton = (Button) view.findViewById(R.id.login_button);
127 | mLogInButton.setOnClickListener(new OnClickListener() {
128 | @Override
129 | public void onClick(View view) {
130 | attemptLogin();
131 | }
132 | });
133 | }
134 |
135 | public void bindTwoFactorAuthFragmentReferences(View view) {
136 | mAuthCodeView = (EditText) view.findViewById(R.id.two_factor_code);
137 | mAuthCodeView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
138 | @Override
139 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
140 | if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
141 | return false;
142 | }
143 |
144 | if (id == R.id.checkTwoFactorCode || id == EditorInfo.IME_NULL) {
145 | attempt2FALogin();
146 | return true;
147 | }
148 | return false;
149 | }
150 | });
151 |
152 | mLogInWithCodeButton = (Button) view.findViewById(R.id.login_button_2fa);
153 | mLogInWithCodeButton.setOnClickListener(new OnClickListener() {
154 | @Override
155 | public void onClick(View view) {
156 | attempt2FALogin();
157 | }
158 | });
159 | }
160 |
161 | @Override
162 | public void onStart() {
163 | super.onStart();
164 | mDispatcher.register(this);
165 | }
166 |
167 | @Override
168 | public void onStop() {
169 | super.onStop();
170 | mDispatcher.unregister(this);
171 | }
172 |
173 | private void attemptLogin() {
174 | if (mUrlIsWPCom) {
175 | Timber.i("Start login process using WPCOM REST API on: " + mUrl);
176 | // WordPress.com login
177 | // TODO: insert cool stuff here
178 | AuthenticatePayload payload = new AuthenticatePayload(mEmailView.getText().toString(),
179 | mPasswordView.getText().toString());
180 | mDispatcher.dispatch(AuthenticationActionBuilder.newAuthenticateAction(payload));
181 | } else {
182 | Timber.i("Start login process using XMLRPC API on: " + mUrl);
183 | // Self Hosted login
184 |
185 | // trigger the discovery process here (if not mUrlIsWPCom, we want to make sure it's a self hosted
186 | // site and not a random site.)
187 | mDispatcher.dispatch(AuthenticationActionBuilder.newDiscoverEndpointAction(mUrl));
188 | }
189 | }
190 |
191 | private void showPostListAndFinish() {
192 | startActivity(new Intent(this, PostListActivity.class));
193 | finish();
194 | }
195 |
196 | private void checkURLField() {
197 | String url = mUrlView.getText().toString();
198 | if (!Patterns.WEB_URL.matcher(url).matches()) {
199 | mUrlView.setError(getText(R.string.error_invalid_url));
200 | return;
201 | }
202 | Timber.i("Start URL check on: " + url);
203 | mUrlView.setEnabled(false);
204 | setProgressVisible(true);
205 | mDispatcher.dispatch(SiteActionBuilder.newIsWpcomUrlAction(url));
206 | }
207 |
208 | private void setEmailPasswordFieldsVisible(boolean visible) {
209 | getFragmentManager().beginTransaction()
210 | .setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out,
211 | android.R.animator.fade_in, android.R.animator.fade_out)
212 | .replace(R.id.fragment_container, new EmailPasswordFragment()).addToBackStack(null).commit();
213 | }
214 |
215 | private void set2FAFieldsVisible(boolean visible) {
216 | getFragmentManager().beginTransaction()
217 | .setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out,
218 | android.R.animator.fade_in, android.R.animator.fade_out)
219 | .replace(R.id.fragment_container, new TwoFactorAuthFragment()).addToBackStack(null).commit();
220 | }
221 |
222 | private void setButtonEnabled(Button button, boolean enabled) {
223 | if (button != null) {
224 | button.setEnabled(enabled);
225 | button.setAlpha(enabled ? 1f : 0.5f);
226 | }
227 | }
228 |
229 | private void setProgressVisible(boolean visible) {
230 | // We don't need a progress bar when we have a rotating egg.
231 | if (visible) {
232 | mImageEggView.animate().setDuration(60000).rotationBy(60 * 360f)
233 | .setInterpolator(new LinearInterpolator()).start();
234 | } else {
235 | mImageEggView.animate().cancel();
236 | }
237 | setButtonEnabled(mLogInButton, !visible);
238 | setButtonEnabled(mNextButton, !visible);
239 | }
240 |
241 | private void attempt2FALogin() {
242 | AuthenticatePayload payload = new AuthenticatePayload(mEmailView.getText().toString(),
243 | mPasswordView.getText().toString());
244 | payload.twoStepCode = mAuthCodeView.getText().toString();
245 | payload.shouldSendTwoStepSms = false;
246 | mDispatcher.dispatch(AuthenticationActionBuilder.newAuthenticateAction(payload));
247 | }
248 |
249 | // FluxC Events
250 |
251 | @SuppressWarnings("unused")
252 | @Subscribe(threadMode = ThreadMode.MAIN)
253 | public void onUrlChecked(OnURLChecked event) {
254 | mUrlView.setEnabled(true);
255 | setProgressVisible(false);
256 |
257 | if (event.isError()) {
258 | Timber.w("onUrlChecked error: " + event.error.type);
259 | mUrlView.setError(getText(R.string.error_invalid_url));
260 | } else {
261 | mUrl = event.url;
262 | mUrlIsWPCom = event.isWPCom;
263 | mUrlValidated = true;
264 | Timber.i("Found a " + (mUrlIsWPCom ? "WPCom" : "Self Hosted or non WordPress") + " site on: " + mUrl);
265 | setEmailPasswordFieldsVisible(true);
266 | }
267 | }
268 |
269 | @SuppressWarnings("unused")
270 | @Subscribe(threadMode = ThreadMode.MAIN)
271 | public void onAuthenticationChanged(OnAuthenticationChanged event) {
272 | if (event.isError()) {
273 | if (event.error.type == AuthenticationErrorType.NEEDS_2FA) {
274 | Timber.i("onAuthenticationChanged error needs 2FA code");
275 | set2FAFieldsVisible(true);
276 | } else {
277 | Timber.i("onAuthenticationChanged error " + event.error.message);
278 | Toast.makeText(this, event.error.message, Toast.LENGTH_SHORT).show();
279 | }
280 | } else {
281 | mDispatcher.dispatch(AccountActionBuilder.newFetchAccountAction());
282 | mDispatcher.dispatch(AccountActionBuilder.newFetchSettingsAction());
283 | mDispatcher.dispatch(SiteActionBuilder.newFetchSitesAction());
284 | }
285 | }
286 |
287 | @SuppressWarnings("unused")
288 | @Subscribe(threadMode = ThreadMode.MAIN)
289 | public void onSiteChanged(OnSiteChanged event) {
290 | if (!event.isError()) {
291 | showPostListAndFinish();
292 | } else {
293 | // TODO
294 | Timber.i("onSiteChanged error");
295 | }
296 | }
297 |
298 | @SuppressWarnings("unused")
299 | @Subscribe(threadMode = ThreadMode.MAIN)
300 | public void onDiscoveryChanged(OnDiscoveryResponse event) {
301 | if (!event.isError()) {
302 | Timber.i("onDiscoveryChanged success " + event.xmlRpcEndpoint);
303 |
304 | mXMLRPCUrl = event.xmlRpcEndpoint;
305 |
306 | if (!TextUtils.isEmpty(mXMLRPCUrl)) {
307 | // now check sites
308 | RefreshSitesXMLRPCPayload refreshSitesXMLRPCPayload = new RefreshSitesXMLRPCPayload();
309 | refreshSitesXMLRPCPayload.username = mEmailView.getText().toString();
310 | refreshSitesXMLRPCPayload.password = mPasswordView.getText().toString();
311 | refreshSitesXMLRPCPayload.url = mXMLRPCUrl;
312 |
313 | mDispatcher.dispatch(SiteActionBuilder.newFetchSitesXmlRpcAction(refreshSitesXMLRPCPayload));
314 | } else {
315 | // TODO show some error
316 | Timber.i("attempt login but we don't have a XMLRPC url - error");
317 | }
318 | } else {
319 | // TODO show error
320 | Timber.i("onDiscoveryChanged error");
321 | }
322 | }
323 | }
324 |
325 |
--------------------------------------------------------------------------------