├── settings.gradle ├── tests └── api │ ├── .gitignore │ ├── test_all.sh │ ├── test_all_auto.sh │ ├── auth_token.js │ ├── package.json │ ├── types.js │ ├── tag.js │ ├── post.js │ └── api.js ├── markdown ├── .gitignore ├── package.json ├── webpack.config.js ├── README.md └── markdown-converter.js ├── .gitattributes ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values-ar │ │ │ │ └── strings.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── appbar_shadow.9.png │ │ │ ├── values-w480dp │ │ │ │ ├── integers.xml │ │ │ │ └── styles.xml │ │ │ ├── values-w720dp │ │ │ │ └── integers.xml │ │ │ ├── values-w960dp │ │ │ │ └── integers.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ ├── activity_login.xml │ │ │ │ ├── fragment_post_view.xml │ │ │ │ ├── fragment_web_view.xml │ │ │ │ ├── fragment_web_view_nested_scroll.xml │ │ │ │ ├── fragment_ghost_auth.xml │ │ │ │ ├── post_list_footer.xml │ │ │ │ ├── dialog_image_insert.xml │ │ │ │ ├── open_source_libs_list_item.xml │ │ │ │ ├── activity_open_source_libs.xml │ │ │ │ ├── fragment_post_edit.xml │ │ │ │ ├── fragment_ghost_v0_error.xml │ │ │ │ └── format_toolbar.xml │ │ │ ├── drawable │ │ │ │ ├── format_italic.xml │ │ │ │ ├── about_rate.xml │ │ │ │ ├── about_community.xml │ │ │ │ ├── status_draft.xml │ │ │ │ ├── image_placeholder.xml │ │ │ │ ├── format_image.xml │ │ │ │ ├── about_open_source_libs.xml │ │ │ │ ├── about_version.xml │ │ │ │ ├── new_draft.xml │ │ │ │ ├── status_scheduled.xml │ │ │ │ ├── format_link.xml │ │ │ │ ├── format_bold.xml │ │ │ │ ├── about_translate.xml │ │ │ │ ├── about_report_bug.xml │ │ │ │ ├── share.xml │ │ │ │ ├── status_published.xml │ │ │ │ ├── settings.xml │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ └── ghost_sad_face.xml │ │ │ ├── menu │ │ │ │ ├── post_edit.xml │ │ │ │ ├── insert_image.xml │ │ │ │ ├── post_list.xml │ │ │ │ └── post_view.xml │ │ │ ├── xml │ │ │ │ └── network_security_config.xml │ │ │ ├── values │ │ │ │ ├── integers.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── themes.xml │ │ │ │ └── dimens.xml │ │ │ ├── values-ro │ │ │ │ └── strings.xml │ │ │ ├── anim-v21 │ │ │ │ └── lift_up.xml │ │ │ ├── values-ml │ │ │ │ └── strings.xml │ │ │ ├── values-ru │ │ │ │ └── strings.xml │ │ │ └── values-vi │ │ │ │ └── strings.xml │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── OpenSans-Bold.ttf │ │ │ │ ├── OpenSans-Italic.ttf │ │ │ │ ├── OpenSans-CondBold.ttf │ │ │ │ ├── OpenSans-Regular.ttf │ │ │ │ └── OpenSans-BoldItalic.ttf │ │ └── java │ │ │ └── me │ │ │ └── vickychijwani │ │ │ └── spectre │ │ │ ├── event │ │ │ ├── LoadTagsEvent.java │ │ │ ├── CredentialsExpiredEvent.java │ │ │ ├── ForceCancelRefreshEvent.java │ │ │ ├── CreatePostEvent.java │ │ │ ├── PostDeletedEvent.java │ │ │ ├── LoginDoneEvent.java │ │ │ ├── FileUploadedEvent.java │ │ │ ├── RefreshDataEvent.java │ │ │ ├── PostSavedEvent.java │ │ │ ├── DeletePostEvent.java │ │ │ ├── PostSyncedEvent.java │ │ │ ├── UserLoadedEvent.java │ │ │ ├── ApiCallEvent.java │ │ │ ├── LoginErrorEvent.java │ │ │ ├── PostCreatedEvent.java │ │ │ ├── PostReplacedEvent.java │ │ │ ├── GhostVersionLoadedEvent.java │ │ │ ├── TagsLoadedEvent.java │ │ │ ├── ApiErrorEvent.java │ │ │ ├── BlogSettingsLoadedEvent.java │ │ │ ├── FileUploadErrorEvent.java │ │ │ ├── LogoutStatusEvent.java │ │ │ ├── DataRefreshedEvent.java │ │ │ ├── LogoutEvent.java │ │ │ ├── PostConflictFoundEvent.java │ │ │ ├── SavePostEvent.java │ │ │ ├── LoadPostsEvent.java │ │ │ ├── LoadUserEvent.java │ │ │ ├── PostsLoadedEvent.java │ │ │ ├── SyncPostsEvent.java │ │ │ ├── LoadBlogSettingsEvent.java │ │ │ ├── LoadGhostVersionEvent.java │ │ │ ├── BusProvider.java │ │ │ └── FileUploadEvent.java │ │ │ ├── network │ │ │ ├── ApiProviderFactory.java │ │ │ ├── ApiProvider.java │ │ │ ├── HttpClientFactory.java │ │ │ ├── entity │ │ │ │ ├── TagStub.java │ │ │ │ ├── ApiErrorList.java │ │ │ │ ├── UserList.java │ │ │ │ ├── SettingsList.java │ │ │ │ ├── ApiError.java │ │ │ │ ├── PostList.kt │ │ │ │ ├── RefreshReqBody.java │ │ │ │ ├── PostStubList.java │ │ │ │ ├── PostStub.java │ │ │ │ ├── RevokeReqBody.java │ │ │ │ ├── ConfigurationList.java │ │ │ │ └── AuthReqBody.java │ │ │ ├── RealmExclusionStrategy.java │ │ │ ├── AnnotationExclusionStrategy.java │ │ │ ├── ApiFailure.java │ │ │ ├── DateDeserializer.java │ │ │ ├── StringConverterFactory.java │ │ │ ├── JSONObjectConverterFactory.java │ │ │ ├── PostTypeAdapterFactory.java │ │ │ ├── ProductionHttpClientFactory.java │ │ │ └── ConfigurationListDeserializer.java │ │ │ ├── util │ │ │ ├── functions │ │ │ │ ├── Action0.java │ │ │ │ ├── Action1.java │ │ │ │ ├── Func1.java │ │ │ │ ├── Action2.java │ │ │ │ └── Action3.java │ │ │ ├── log │ │ │ │ ├── DebugLogger.java │ │ │ │ ├── JvmLogger.java │ │ │ │ ├── ReleaseLogger.java │ │ │ │ ├── Logger.java │ │ │ │ └── Log.java │ │ │ ├── DeviceUtils.java │ │ │ ├── Listenable.java │ │ │ ├── DateTimeUtils.java │ │ │ └── EditTextSelectionState.java │ │ │ ├── model │ │ │ ├── DBConfiguration.java │ │ │ ├── BlogMetadataModule.java │ │ │ ├── GsonExclude.java │ │ │ ├── entity │ │ │ │ ├── Role.java │ │ │ │ ├── ConfigurationParam.java │ │ │ │ ├── Setting.java │ │ │ │ ├── ETag.java │ │ │ │ ├── PendingAction.java │ │ │ │ └── User.java │ │ │ ├── BlogDataModule.java │ │ │ └── BlogDBMigration.java │ │ │ ├── error │ │ │ ├── PostConflictFoundException.java │ │ │ ├── UserEditsLostException.java │ │ │ ├── UncaughtRxException.java │ │ │ ├── UrlNotFoundException.java │ │ │ ├── LoginFailedException.java │ │ │ ├── SyncException.java │ │ │ ├── FileUploadFailedException.java │ │ │ └── TokenRevocationFailedException.java │ │ │ ├── auth │ │ │ ├── PasswordAuth.java │ │ │ ├── BlogUrlValidator.java │ │ │ ├── ProductionApiProviderFactory.java │ │ │ ├── CredentialSource.java │ │ │ ├── CredentialSink.java │ │ │ ├── ProductionApiProvider.java │ │ │ ├── GhostAuth.java │ │ │ └── AuthStore.java │ │ │ ├── view │ │ │ ├── FormatOptionClickListener.java │ │ │ ├── BundleKeys.java │ │ │ ├── widget │ │ │ │ └── SpaceItemDecoration.java │ │ │ ├── fragments │ │ │ │ └── GhostV0ErrorFragment.java │ │ │ ├── AboutActivity.java │ │ │ ├── image │ │ │ │ └── BorderedCircleTransformation.java │ │ │ └── Observables.java │ │ │ └── pref │ │ │ ├── BaseKey.java │ │ │ ├── AppState.java │ │ │ ├── UserPrefs.java │ │ │ └── Prefs.java │ ├── debug │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── vickychijwani │ │ │ └── spectre │ │ │ └── testing │ │ │ ├── EventBusRule.kt │ │ │ ├── JvmLoggingRule.kt │ │ │ ├── UrlMatches.kt │ │ │ ├── RxSchedulersRule.kt │ │ │ └── Helpers.kt │ ├── androidTest │ │ └── java │ │ │ └── me │ │ │ └── vickychijwani │ │ │ └── spectre │ │ │ └── testing │ │ │ ├── BaseIdlingResource.kt │ │ │ ├── ViewNotVisibleIdlingResource.kt │ │ │ ├── ViewDoesntMatchTextIdlingResource.kt │ │ │ ├── OkHttpIdlingResourceRule.kt │ │ │ ├── EndToEndTestUtils.kt │ │ │ ├── ActivityStateIdlingResource.kt │ │ │ ├── ClearPreferencesRule.kt │ │ │ ├── RecyclerViewMatcher.kt │ │ │ ├── OkHttpIdlingResource.kt │ │ │ └── LoginRobot.kt │ └── sharedTest │ │ └── java │ │ └── me │ │ └── vickychijwani │ │ └── spectre │ │ └── testing │ │ ├── DeleteDefaultPostsRule.kt │ │ └── GhostApiTestUtils.kt ├── lint.xml └── proguard-rules.pro ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── ISSUE_TEMPLATE.md ├── CONTRIBUTING.md ├── gradle.properties ├── LICENSE ├── .circleci └── config.yml └── gradlew.bat /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /tests/api/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /markdown/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | app/src/main/assets/markdown-converter.js binary 2 | 3 | -------------------------------------------------------------------------------- /tests/api/test_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | jasmine-node . --growl 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /crashlytics.properties 3 | /fabric.properties 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/api/test_all_auto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | jasmine-node . --growl --autotest --watch . --color 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | *.iml 5 | **/*.iml 6 | .DS_Store 7 | /build 8 | /captures/ 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/assets/fonts/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/assets/fonts/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/assets/fonts/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | FILL THESE DETAILS FIRST: 2 | 3 | - Ghost version: X.Y.Z 4 | - App version: X.Y.Z 5 | 6 | ---- 7 | 8 | DESCRIBE YOUR ISSUE HERE 9 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/OpenSans-CondBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/assets/fonts/OpenSans-CondBold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/assets/fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/assets/fonts/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/assets/fonts/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoadTagsEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoadTagsEvent { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/appbar_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/drawable-xxhdpi/appbar_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TryGhost/Ghost-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/CredentialsExpiredEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class CredentialsExpiredEvent { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/ForceCancelRefreshEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class ForceCancelRefreshEvent { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-w480dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w720dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w960dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Ghost (Dev) 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/CreatePostEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class CreatePostEvent { 4 | 5 | public CreatePostEvent() {} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/ApiProviderFactory.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network; 2 | 3 | public interface ApiProviderFactory { 4 | 5 | ApiProvider create(String blogUrl); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /tests/api/auth_token.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash-node'); 2 | 3 | module.exports = {}; 4 | 5 | module.exports.schema = { 6 | access_token: String, 7 | expires_in: Number, 8 | token_type: 'Bearer', 9 | }; 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/functions/Action0.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.functions; 2 | 3 | /** 4 | * A zero-argument action. 5 | */ 6 | public interface Action0 { 7 | 8 | void call(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/functions/Action1.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.functions; 2 | 3 | /** 4 | * A one-argument action. 5 | */ 6 | public interface Action1 { 7 | 8 | void call(T t); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/functions/Func1.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.functions; 2 | 3 | /** 4 | * A function with one argument. 5 | */ 6 | public interface Func1 { 7 | 8 | R call(T t); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/functions/Action2.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.functions; 2 | 3 | /** 4 | * A two-argument action. 5 | */ 6 | public interface Action2 { 7 | 8 | void call(T1 t1, T2 t2); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/functions/Action3.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.functions; 2 | 3 | /** 4 | * A three-argument action. 5 | */ 6 | public interface Action3 { 7 | 8 | void call(T1 t1, T2 t2, T3 t3); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/ApiProvider.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network; 2 | 3 | import retrofit2.Retrofit; 4 | 5 | public interface ApiProvider { 6 | 7 | Retrofit getRetrofit(); 8 | 9 | GhostApiService getGhostApi(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Feb 11 23:30:42 IST 2018 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.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostDeletedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class PostDeletedEvent { 4 | 5 | public final String postId; 6 | 7 | public PostDeletedEvent(String postId) { 8 | this.postId = postId; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoginDoneEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoginDoneEvent { 4 | 5 | public final String blogUrl; 6 | 7 | public LoginDoneEvent(String blogUrl) { 8 | this.blogUrl = blogUrl; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/model/DBConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.model; 2 | 3 | public abstract class DBConfiguration { 4 | 5 | public static final int METADATA_DB_SCHEMA_VERSION = 4; 6 | 7 | public static final int DATA_DB_SCHEMA_VERSION = 4; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quill-ghost-api-tests", 3 | "scripts": { 4 | "test": "echo \"Error: no test specified\" && exit 1" 5 | }, 6 | "dependencies": { 7 | "frisby": "^0.8.5", 8 | "lodash-node": "^3.10.0", 9 | "request-json": "^0.5.3", 10 | "x-ray": "^2.0.2" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/FileUploadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class FileUploadedEvent { 4 | 5 | public final String relativeUrl; 6 | 7 | public FileUploadedEvent(String relativeUrl) { 8 | this.relativeUrl = relativeUrl; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/PostConflictFoundException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public class PostConflictFoundException extends RuntimeException { 4 | 5 | public PostConflictFoundException() { 6 | super("POST CONFLICT FOUND: see logs for details"); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/RefreshDataEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class RefreshDataEvent { 4 | 5 | public final boolean loadCachedData; 6 | 7 | public RefreshDataEvent(boolean loadCachedData) { 8 | this.loadCachedData = loadCachedData; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/UserEditsLostException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public final class UserEditsLostException extends RuntimeException { 4 | 5 | public UserEditsLostException() { 6 | super("USER EDITS WERE OVERWRITTEN! SEE STACK TO TRACE THE ROOT CAUSE."); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostSavedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class PostSavedEvent { 6 | 7 | public final Post post; 8 | 9 | public PostSavedEvent(Post post) { 10 | this.post = post; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/DeletePostEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class DeletePostEvent { 6 | 7 | public final Post post; 8 | 9 | public DeletePostEvent(Post post) { 10 | this.post = post; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostSyncedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class PostSyncedEvent { 6 | 7 | public final Post post; 8 | 9 | public PostSyncedEvent(Post post) { 10 | this.post = post; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/UserLoadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.User; 4 | 5 | public class UserLoadedEvent { 6 | 7 | public final User user; 8 | 9 | public UserLoadedEvent(User user) { 10 | this.user = user; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/HttpClientFactory.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import java.io.File; 6 | 7 | import okhttp3.OkHttpClient; 8 | 9 | public interface HttpClientFactory { 10 | 11 | OkHttpClient create(@Nullable File cacheDir); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/ApiCallEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public interface ApiCallEvent { 4 | 5 | /** 6 | * Indicate to the receiver that a network call will fail (network errors, site down) and 7 | * therefore should NOT be attempted 8 | */ 9 | void loadCachedData(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoginErrorEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | public class LoginErrorEvent { 6 | 7 | public final String blogUrl; 8 | 9 | public LoginErrorEvent(@Nullable String blogUrl) { 10 | this.blogUrl = blogUrl; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostCreatedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class PostCreatedEvent { 6 | 7 | public final Post newPost; 8 | 9 | public PostCreatedEvent(Post newPost) { 10 | this.newPost = newPost; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostReplacedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class PostReplacedEvent { 6 | 7 | public final Post newPost; 8 | 9 | public PostReplacedEvent(Post newPost) { 10 | this.newPost = newPost; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/auth/PasswordAuth.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.auth; 2 | 3 | public final class PasswordAuth { 4 | 5 | public static class Params { 6 | 7 | public final String blogUrl; 8 | 9 | public Params(String blogUrl) { 10 | this.blogUrl = blogUrl; 11 | } 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/UncaughtRxException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public class UncaughtRxException extends RuntimeException { 4 | 5 | /** 6 | * @param cause - the original cause of this exception 7 | */ 8 | public UncaughtRxException(Throwable cause) { 9 | super(cause); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/view/FormatOptionClickListener.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.view; 2 | 3 | import android.view.View; 4 | 5 | public interface FormatOptionClickListener { 6 | void onFormatBoldClicked(View v); 7 | void onFormatItalicClicked(View v); 8 | void onFormatLinkClicked(View v); 9 | void onFormatImageClicked(View v); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/UrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public class UrlNotFoundException extends RuntimeException { 4 | 5 | /** 6 | * @param url - the URL that could not be found 7 | */ 8 | public UrlNotFoundException(String url) { 9 | super("URL RETURNED 404: " + url); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/GhostVersionLoadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | public class GhostVersionLoadedEvent { 6 | 7 | public final String version; 8 | 9 | public GhostVersionLoadedEvent(@NonNull String version) { 10 | this.version = version; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/TagsLoadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import java.util.List; 4 | 5 | import me.vickychijwani.spectre.model.entity.Tag; 6 | 7 | public class TagsLoadedEvent { 8 | 9 | public final List tags; 10 | 11 | public TagsLoadedEvent(List tags) { 12 | this.tags = tags; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/model/BlogMetadataModule.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.model; 2 | 3 | import io.realm.annotations.RealmModule; 4 | import me.vickychijwani.spectre.model.entity.BlogMetadata; 5 | 6 | // set of classes included in the schema for blog metadata Realm 7 | 8 | @RealmModule(classes = { 9 | BlogMetadata.class 10 | }) 11 | public class BlogMetadataModule {} 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/format_italic.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_post_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/post_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/ApiErrorEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import me.vickychijwani.spectre.network.ApiFailure; 6 | 7 | public class ApiErrorEvent { 8 | 9 | public final ApiFailure apiFailure; 10 | 11 | public ApiErrorEvent(@NonNull ApiFailure apiFailure) { 12 | this.apiFailure = apiFailure; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/BlogSettingsLoadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import java.util.List; 4 | 5 | import me.vickychijwani.spectre.model.entity.Setting; 6 | 7 | public class BlogSettingsLoadedEvent { 8 | 9 | public final List settings; 10 | 11 | public BlogSettingsLoadedEvent(List settings) { 12 | this.settings = settings; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/FileUploadErrorEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.network.ApiFailure; 4 | 5 | @SuppressWarnings({"WeakerAccess", "unused"}) 6 | public class FileUploadErrorEvent { 7 | 8 | public final ApiFailure apiFailure; 9 | 10 | public FileUploadErrorEvent(ApiFailure apiFailure) { 11 | this.apiFailure = apiFailure; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LogoutStatusEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LogoutStatusEvent { 4 | 5 | public final boolean succeeded; 6 | public final boolean hasPendingActions; 7 | 8 | public LogoutStatusEvent(boolean succeeded, boolean hasPendingActions) { 9 | this.succeeded = succeeded; 10 | this.hasPendingActions = hasPendingActions; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tests/api/types.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | StringOrNull: function (val) { return expect(val).toBeTypeOrNull(String); }, 3 | NumberOrNull: function (val) { return expect(val).toBeTypeOrNull(Number); }, 4 | DateString: function (val) { return expect(val).toMatch(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ$/); }, 5 | DateStringOrNull: function (val) { return expect(val).toMatchOrBeNull(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ$/); }, 6 | }; 7 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/DataRefreshedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import me.vickychijwani.spectre.network.ApiFailure; 6 | 7 | public class DataRefreshedEvent { 8 | 9 | public final ApiFailure apiFailure; 10 | 11 | public DataRefreshedEvent(@Nullable ApiFailure apiFailure) { 12 | this.apiFailure = apiFailure; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_rate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/model/GsonExclude.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.model; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | // credits: http://stackoverflow.com/a/27986860/504611 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target(ElementType.FIELD) 11 | public @interface GsonExclude { 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/TagStub.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import me.vickychijwani.spectre.model.entity.Tag; 6 | 7 | @SuppressWarnings({"WeakerAccess", "unused"}) 8 | public class TagStub { 9 | 10 | public final String name; 11 | 12 | public TagStub(@NonNull Tag tag) { 13 | this.name = tag.getName(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LogoutEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | public class LogoutEvent { 6 | 7 | @NonNull public final String blogUrl; 8 | public final boolean forceLogout; 9 | 10 | public LogoutEvent(@NonNull String blogUrl, boolean forceLogout) { 11 | this.blogUrl = blogUrl; 12 | this.forceLogout = forceLogout; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tests/api/tag.js: -------------------------------------------------------------------------------- 1 | var types = require('./types'); 2 | 3 | module.exports = {}; 4 | 5 | module.exports.schema = { 6 | uuid: String, 7 | name: String, 8 | 9 | slug: String, 10 | description: types.StringOrNull, 11 | image: types.StringOrNull, 12 | hidden: Boolean, 13 | 14 | meta_title: types.StringOrNull, 15 | meta_description: types.StringOrNull, 16 | 17 | created_at: types.DateString, 18 | updated_at: types.DateString, 19 | }; 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Reporting bugs 2 | 3 | **Please follow these guidelines when creating a new issue:** 4 | 5 | - Take some time to check for related issues before you create a new one. 6 | - Describe your issue in as much detail as possible. 7 | - Always include these details in bug reports: app version, Android version, device name. **Reports without these details may be ignored.** 8 | 9 | **[VIEW OR CREATE ISSUES HERE](https://github.com/TryGhost/Ghost-Android/issues)** 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_community.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/status_draft.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_placeholder.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-w480dp/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 769 5 | 770 6 | 200 7 | 1 8 | 9 | 10 | 300 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostConflictFoundEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public final class PostConflictFoundEvent { 6 | 7 | public final Post localPost; 8 | public final Post serverPost; 9 | 10 | public PostConflictFoundEvent(Post localPost, Post serverPost) { 11 | this.localPost = localPost; 12 | this.serverPost = serverPost; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/format_image.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/SavePostEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import me.vickychijwani.spectre.model.entity.Post; 4 | 5 | public class SavePostEvent { 6 | 7 | public final Post post; 8 | public final boolean isAutoSave; // was this post saved automatically or explicitly? 9 | 10 | public SavePostEvent(Post post, boolean isAutoSave) { 11 | this.post = post; 12 | this.isAutoSave = isAutoSave; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_open_source_libs.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/log/DebugLogger.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.log; 2 | 3 | final class DebugLogger extends Logger { 4 | 5 | @Override 6 | public void log(int priority, String tag, String message) { 7 | android.util.Log.println(priority, tag, message); 8 | } 9 | 10 | @Override 11 | public void exception(Throwable error) { 12 | android.util.Log.e("Exception", android.util.Log.getStackTraceString(error)); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoadPostsEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoadPostsEvent implements ApiCallEvent { 4 | 5 | public final boolean forceNetworkCall; 6 | public boolean loadCachedData = false; 7 | 8 | public LoadPostsEvent(boolean forceNetworkCall) { 9 | this.forceNetworkCall = forceNetworkCall; 10 | } 11 | 12 | @Override 13 | public void loadCachedData() { 14 | loadCachedData = true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoadUserEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoadUserEvent implements ApiCallEvent { 4 | 5 | public final boolean forceNetworkCall; 6 | public boolean loadCachedData = false; 7 | 8 | public LoadUserEvent(boolean forceNetworkCall) { 9 | this.forceNetworkCall = forceNetworkCall; 10 | } 11 | 12 | @Override 13 | public void loadCachedData() { 14 | loadCachedData = true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/PostsLoadedEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import java.util.List; 4 | 5 | import me.vickychijwani.spectre.model.entity.Post; 6 | 7 | public class PostsLoadedEvent { 8 | 9 | public final List posts; 10 | public final int postsFetchLimit; 11 | 12 | public PostsLoadedEvent(List posts, int postsFetchLimit) { 13 | this.posts = posts; 14 | this.postsFetchLimit = postsFetchLimit; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/SyncPostsEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class SyncPostsEvent implements ApiCallEvent { 4 | 5 | public final boolean forceNetworkCall; 6 | public boolean loadCachedData = false; 7 | 8 | public SyncPostsEvent(boolean forceNetworkCall) { 9 | this.forceNetworkCall = forceNetworkCall; 10 | } 11 | 12 | @Override 13 | public void loadCachedData() { 14 | loadCachedData = true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/ApiErrorList.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | @SuppressWarnings("unused") 7 | public class ApiErrorList { 8 | 9 | public List errors; 10 | 11 | public static ApiErrorList from(ApiError... errors) { 12 | ApiErrorList errorList = new ApiErrorList(); 13 | errorList.errors = Arrays.asList(errors); 14 | return errorList; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/log/JvmLogger.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util.log; 2 | 3 | /** 4 | * Use this for code running on a JVM instead of on Android (e.g., unit tests). 5 | */ 6 | final class JvmLogger extends Logger { 7 | 8 | @Override 9 | public void log(int priority, String tag, String message) { 10 | System.out.println(message); 11 | } 12 | 13 | @Override 14 | public void exception(Throwable error) { 15 | error.printStackTrace(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoadBlogSettingsEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoadBlogSettingsEvent implements ApiCallEvent { 4 | 5 | public final boolean forceNetworkCall; 6 | public boolean loadCachedData = false; 7 | 8 | public LoadBlogSettingsEvent(boolean forceNetworkCall) { 9 | this.forceNetworkCall = forceNetworkCall; 10 | } 11 | 12 | @Override 13 | public void loadCachedData() { 14 | loadCachedData = true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/LoadGhostVersionEvent.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | public class LoadGhostVersionEvent implements ApiCallEvent { 4 | 5 | public final boolean forceNetworkCall; 6 | public boolean loadCachedData = false; 7 | 8 | public LoadGhostVersionEvent(boolean forceNetworkCall) { 9 | this.forceNetworkCall = forceNetworkCall; 10 | } 11 | 12 | @Override 13 | public void loadCachedData() { 14 | loadCachedData = true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/view/BundleKeys.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.view; 2 | 3 | public final class BundleKeys { 4 | 5 | public static final String POST = "key:post"; 6 | public static final String START_EDITING = "key:start_editing"; 7 | public static final String URL = "key:url"; 8 | public static final String POST_EDITED = "key:post_edited"; 9 | 10 | public static final String LOCAL_POST = "key:local_post"; 11 | public static final String SERVER_POST = "key:server_post"; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_version.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/new_draft.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/status_scheduled.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /markdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ghost-android-markdown", 3 | "description": "Builds the Markdown-to-HTML converter used by Ghost Android", 4 | "repository": { 5 | "type": "git", 6 | "url": "git@github.com:TryGhost/Ghost-Android.git" 7 | }, 8 | "scripts": { 9 | "build": "./node_modules/.bin/webpack --optimize-minimize" 10 | }, 11 | "devDependencies": { 12 | "markdown-it": "8.4.1", 13 | "markdown-it-footnote": "3.0.1", 14 | "markdown-it-lazy-headers": "0.1.3", 15 | "markdown-it-mark": "2.0.0", 16 | "webpack": "^3.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/util/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.support.annotation.NonNull; 6 | 7 | public class DeviceUtils { 8 | 9 | public static int getScreenWidth(@NonNull Context context) { 10 | return context.getResources().getDisplayMetrics().widthPixels; 11 | } 12 | 13 | public static float dpToPx(final float dp) { 14 | return dp * Resources.getSystem().getDisplayMetrics().density; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_web_view.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/event/BusProvider.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.event; 2 | 3 | import android.support.annotation.RestrictTo; 4 | 5 | import com.squareup.otto.Bus; 6 | import com.squareup.otto.ThreadEnforcer; 7 | 8 | public class BusProvider { 9 | 10 | private static Bus sBus = new Bus(); 11 | 12 | private BusProvider() {} 13 | 14 | @RestrictTo(RestrictTo.Scope.TESTS) 15 | public static void setupForTesting() { 16 | sBus = new Bus(ThreadEnforcer.ANY); 17 | } 18 | 19 | public static Bus getBus() { return sBus; } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_web_view_nested_scroll.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/UserList.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import me.vickychijwani.spectre.model.entity.User; 7 | 8 | // dummy wrapper class needed for Retrofit 9 | @SuppressWarnings("unused") 10 | public class UserList { 11 | 12 | public List users; 13 | 14 | public static UserList from(User... users) { 15 | UserList userList = new UserList(); 16 | userList.users = Arrays.asList(users); 17 | return userList; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/format_link.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/SettingsList.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import me.vickychijwani.spectre.model.entity.Setting; 7 | 8 | @SuppressWarnings("unused") 9 | public class SettingsList { 10 | 11 | public List settings; 12 | 13 | public static SettingsList from(Setting... settings) { 14 | SettingsList settingsList = new SettingsList(); 15 | settingsList.settings = Arrays.asList(settings); 16 | return settingsList; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/RealmExclusionStrategy.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | 6 | import io.realm.RealmObject; 7 | 8 | class RealmExclusionStrategy implements ExclusionStrategy { 9 | 10 | @Override 11 | public boolean shouldSkipField(FieldAttributes f) { 12 | return f.getDeclaringClass().equals(RealmObject.class); 13 | } 14 | 15 | @Override 16 | public boolean shouldSkipClass(Class clazz) { 17 | return false; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/ApiError.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | @SuppressWarnings("unused") 6 | public class ApiError { 7 | 8 | public String message; // meant for humans 9 | 10 | // unfortunately there is a bug in the field name on the API side, it is "errorType" rather than 11 | // "error_type", hence the custom @SerializedName 12 | @SerializedName("errorType") 13 | public String errorType; // meant for machines: "UnauthorizedError", etc. 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/network/entity/PostList.kt: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.network.entity 2 | 3 | import me.vickychijwani.spectre.model.entity.Post 4 | 5 | // dummy wrapper class needed for Retrofit 6 | class PostList(var posts: MutableList) { 7 | 8 | operator fun contains(id: String): Boolean { 9 | for (post in posts) { 10 | if (id == post.id) { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | 17 | fun remove(idx: Int): Post { 18 | return posts.removeAt(idx) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/format_bold.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/pref/BaseKey.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.pref; 2 | 3 | abstract class BaseKey { 4 | 5 | private final String mStr; 6 | private final Class mType; 7 | private final Object mDefaultValue; 8 | 9 | /* package */ BaseKey(String str, Class type, T defaultValue) { 10 | mStr = str; 11 | mType = type; 12 | mDefaultValue = defaultValue; 13 | } 14 | 15 | public Object getDefaultValue() { return mDefaultValue; } 16 | public Class getType() { return mType; } 17 | public String toString() { return mStr; } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/LoginFailedException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public final class LoginFailedException extends RuntimeException { 4 | 5 | /** 6 | * @param throwable - the cause of this login failure 7 | */ 8 | public LoginFailedException(Throwable throwable) { 9 | super("LOGIN FAILED: see previous exception for details", throwable); 10 | } 11 | 12 | /** 13 | * @param message a custom String message 14 | */ 15 | public LoginFailedException(String message) { 16 | super("LOGIN FAILED: " + message); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/me/vickychijwani/spectre/error/SyncException.java: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.error; 2 | 3 | public final class SyncException extends RuntimeException { 4 | 5 | /** 6 | * @param throwable - the cause of this exception 7 | */ 8 | public SyncException(String message, Throwable throwable) { 9 | super("SYNC EXCEPTION: " + message + ", see previous exception for details", throwable); 10 | } 11 | 12 | /** 13 | * @param message a custom String message 14 | */ 15 | public SyncException(String message) { 16 | super("SYNC EXCEPTION: " + message); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/test/java/me/vickychijwani/spectre/testing/EventBusRule.kt: -------------------------------------------------------------------------------- 1 | package me.vickychijwani.spectre.testing 2 | 3 | import me.vickychijwani.spectre.event.BusProvider 4 | import org.junit.rules.TestRule 5 | import org.junit.runner.Description 6 | import org.junit.runners.model.Statement 7 | 8 | class EventBusRule : TestRule { 9 | 10 | override fun apply(base: Statement, description: Description): Statement { 11 | return object : Statement() { 12 | override fun evaluate() { 13 | BusProvider.setupForTesting() 14 | base.evaluate() 15 | } 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /markdown/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: './markdown-converter.js', 5 | // the generated converter will be usable as MarkdownConverter.render("**Markdown** _string_") 6 | // when included in a