├── app
├── .gitignore
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── src
│ ├── local
│ │ └── res
│ │ │ └── raw
│ │ │ └── app_config.json
│ ├── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── ag_avatar.png
│ │ │ │ ├── ic_lock.png
│ │ │ │ ├── baseline_warning.png
│ │ │ │ ├── ic_openid_connect.png
│ │ │ │ ├── baseline_verified_user.png
│ │ │ │ ├── authentication_background.png
│ │ │ │ ├── menu_color.xml
│ │ │ │ ├── ic_home.xml
│ │ │ │ ├── ic_clear.xml
│ │ │ │ ├── menu_nav_bar.xml
│ │ │ │ ├── ic_delete.xml
│ │ │ │ ├── ic_person.xml
│ │ │ │ ├── ic_config.xml
│ │ │ │ ├── ic_storage.xml
│ │ │ │ ├── background_gradient.xml
│ │ │ │ ├── ic_device_os.xml
│ │ │ │ ├── ic_security.xml
│ │ │ │ ├── ic_message.xml
│ │ │ │ ├── ic_insert_chart.xml
│ │ │ │ ├── ic_note_add.xml
│ │ │ │ ├── ic_refresh.xml
│ │ │ │ ├── ic_crypto.xml
│ │ │ │ ├── ic_memory.xml
│ │ │ │ ├── profile_image_border.xml
│ │ │ │ ├── ic_account_circle.xml
│ │ │ │ ├── ic_http.xml
│ │ │ │ ├── ic_lock_open.xml
│ │ │ │ ├── ic_hooking.xml
│ │ │ │ ├── ic_network_locked.xml
│ │ │ │ ├── ic_phone_lock.xml
│ │ │ │ ├── ic_notifications_active.xml
│ │ │ │ ├── fh_icon.xml
│ │ │ │ └── ic_error_background.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
│ │ │ ├── layout
│ │ │ │ ├── view_progress.xml
│ │ │ │ ├── fragment_not_available_dialog.xml
│ │ │ │ ├── item_http.xml
│ │ │ │ ├── fragment_warning_title.xml
│ │ │ │ ├── fragment_not_available_title.xml
│ │ │ │ ├── push_registration_failed_title.xml
│ │ │ │ ├── fragment_push.xml
│ │ │ │ ├── menu_app_bar.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── fragment_notes_list.xml
│ │ │ │ ├── content_area.xml
│ │ │ │ ├── item_landing.xml
│ │ │ │ ├── fragment_note.xml
│ │ │ │ ├── fragment_home.xml
│ │ │ │ ├── menu_nav_header.xml
│ │ │ │ ├── item_push.xml
│ │ │ │ ├── fragment_authentication.xml
│ │ │ │ ├── fragment_landing_security.xml
│ │ │ │ ├── fragment_under_construction.xml
│ │ │ │ ├── fragment_network.xml
│ │ │ │ └── fragment_landing.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ └── menu
│ │ │ │ └── menu_sidebar_content.xml
│ │ ├── assets
│ │ │ └── mobile-services.json
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── aerogear
│ │ │ │ └── androidshowcase
│ │ │ │ ├── navigation
│ │ │ │ ├── GotoDocsListener.java
│ │ │ │ ├── PushRegistrationFailedDialogFragment.java
│ │ │ │ └── NotAvailableDialogFragment.java
│ │ │ │ ├── features
│ │ │ │ ├── push
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── PushView.java
│ │ │ │ │ │ └── PushViewImpl.java
│ │ │ │ │ ├── presenters
│ │ │ │ │ │ └── PushPresenter.java
│ │ │ │ │ ├── PushGestureViewHolder.java
│ │ │ │ │ └── PushGestureAdapter.java
│ │ │ │ ├── landing
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── LandingView.java
│ │ │ │ │ │ └── LandingViewImpl.java
│ │ │ │ │ ├── presenters
│ │ │ │ │ │ └── LandingPresenter.java
│ │ │ │ │ └── LandingFragment.java
│ │ │ │ ├── documentation
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── DocumentationView.java
│ │ │ │ │ │ └── DocumentationViewImpl.java
│ │ │ │ │ ├── presenters
│ │ │ │ │ │ └── DocumentationPresenter.java
│ │ │ │ │ ├── DocumentUrl.java
│ │ │ │ │ └── DocumentationFragment.java
│ │ │ │ ├── underconstruction
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── UnderConstructionView.java
│ │ │ │ │ │ └── UnderConstructionViewImpl.java
│ │ │ │ │ ├── presenters
│ │ │ │ │ │ └── UnderConstructionPresenter.java
│ │ │ │ │ └── UnderConstructionFragment.java
│ │ │ │ ├── home
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── HomeView.java
│ │ │ │ │ │ └── HomeViewImpl.java
│ │ │ │ │ ├── presenters
│ │ │ │ │ │ └── HomeViewPresenter.java
│ │ │ │ │ └── HomeFragment.java
│ │ │ │ ├── device
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── DeviceView.java
│ │ │ │ │ │ ├── DeviceViewImpl.java
│ │ │ │ │ │ └── WarningDialog.java
│ │ │ │ │ └── presenters
│ │ │ │ │ │ └── DevicePresenter.java
│ │ │ │ └── authentication
│ │ │ │ │ ├── views
│ │ │ │ │ ├── AuthenticationView.java
│ │ │ │ │ ├── AuthenticationDetailsView.java
│ │ │ │ │ ├── AuthenticationViewImpl.java
│ │ │ │ │ └── AuthenticationDetailsViewImpl.java
│ │ │ │ │ ├── providers
│ │ │ │ │ ├── OpenIDAuthenticationProvider.java
│ │ │ │ │ └── KeycloakAuthenticateProviderImpl.java
│ │ │ │ │ └── presenters
│ │ │ │ │ ├── AuthenticationDetailsPresenter.java
│ │ │ │ │ └── AuthenticationViewPresenter.java
│ │ │ │ ├── BaseActivity.java
│ │ │ │ ├── domain
│ │ │ │ ├── callbacks
│ │ │ │ │ └── CallbackHandler.java
│ │ │ │ ├── configurations
│ │ │ │ │ ├── ApiServerConfiguration.java
│ │ │ │ │ └── AppConfiguration.java
│ │ │ │ ├── models
│ │ │ │ │ └── User.java
│ │ │ │ ├── utils
│ │ │ │ │ └── StreamUtils.java
│ │ │ │ ├── crypto
│ │ │ │ │ ├── SecureKeyStoreImpl.java
│ │ │ │ │ ├── RsaCrypto.java
│ │ │ │ │ ├── NullAndroidSecureKeyStore.java
│ │ │ │ │ ├── RsaHelper.java
│ │ │ │ │ ├── SecureKeyStore.java
│ │ │ │ │ └── AndroidMSecureKeyStore.java
│ │ │ │ └── Constants.java
│ │ │ │ ├── di
│ │ │ │ ├── MainActivityModule.java
│ │ │ │ ├── SecureApplicationComponent.java
│ │ │ │ ├── FragmentModules.java
│ │ │ │ └── SecureApplicationModule.java
│ │ │ │ ├── mvp
│ │ │ │ ├── presenters
│ │ │ │ │ ├── BasePresenter.java
│ │ │ │ │ └── Presenter.java
│ │ │ │ ├── views
│ │ │ │ │ ├── AppView.java
│ │ │ │ │ ├── BaseFragment.java
│ │ │ │ │ └── BaseAppView.java
│ │ │ │ └── components
│ │ │ │ │ ├── ProgressDialogHelper.java
│ │ │ │ │ ├── HttpHelper.java
│ │ │ │ │ └── CertPinningHelper.java
│ │ │ │ ├── SecureApplication.java
│ │ │ │ ├── providers
│ │ │ │ └── PushServiceProvider.java
│ │ │ │ ├── CertificateErrorDialog.java
│ │ │ │ └── handler
│ │ │ │ └── NotificationBarMessageHandler.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── aerogear
│ │ │ └── androidshowcase
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── aerogear
│ │ └── androidshowcase
│ │ ├── di
│ │ ├── SecureTestApplication.java
│ │ ├── SecureApplicationTestComponent.java
│ │ └── SecureApplicationTestModule.java
│ │ ├── MockTestRunner.java
│ │ ├── RsaCryptoTest.java
│ │ ├── domain
│ │ └── store
│ │ │ ├── sqlite
│ │ │ └── SqliteNoteStoreTest.java
│ │ │ ├── SecureFileNoteStoreTest.java
│ │ │ └── NoteStoreTestBase.java
│ │ ├── MainActivityInstrumentedTest.java
│ │ └── AesCryptoTest.java
├── .classpath
├── .project
├── proguard-rules.pro
└── google-services.json
├── settings.gradle
├── .settings
└── org.eclipse.buildship.core.prefs
├── signing_keys.keystore
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── CODE_OF_CONDUCT.md
├── CODE_OF_CONDUCT
└── CONTRIBUTING.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── pushToKryptowire.sh
├── .project
├── gradle.properties
├── .circleci
└── config.yml
├── Jenkinsfile
├── gradlew.bat
├── readme.adoc
└── CONTRIBUTING.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | #Tue Sep 05 10:43:45 IST 2017
2 | connection.project.dir=
3 |
--------------------------------------------------------------------------------
/app/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | #Tue Sep 05 10:43:45 IST 2017
2 | connection.project.dir=..
3 |
--------------------------------------------------------------------------------
/signing_keys.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/signing_keys.keystore
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | ## Default owners (going to be removed at later stage)
2 | * @danielpassos @secondsun @wei-lee
3 |
--------------------------------------------------------------------------------
/app/src/local/res/raw/app_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "api-server": {
3 | "server-url": "http://www.rhdev.me:8080"
4 | }
5 | }
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ag_avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/ag_avatar.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/ic_lock.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .vscode
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/baseline_warning.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_openid_connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/ic_openid_connect.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/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/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_verified_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/baseline_verified_user.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/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/aerogear/android-showcase-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/authentication_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aerogear/android-showcase-template/HEAD/app/src/main/res/drawable/authentication_background.png
--------------------------------------------------------------------------------
/app/src/main/assets/mobile-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "clusterName": "https://192.168.42.208:8443",
4 | "namespace": "showcase",
5 | "clientId": "myapp-android",
6 | "services": [
7 | ]
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/navigation/GotoDocsListener.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.navigation;
2 |
3 | @FunctionalInterface
4 | interface GotoDocsListener {
5 | void goToDocs();
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/push/views/PushView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.push.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | public interface PushView extends AppView {
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/landing/views/LandingView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.landing.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | public interface LandingView extends AppView {
6 | }
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 27 19:23:52 EDT 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.6-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/documentation/views/DocumentationView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.documentation.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | public interface DocumentationView extends AppView {
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/menu_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 |
5 | /**
6 | * Created by weili on 12/09/2017.
7 | */
8 |
9 | public class BaseActivity extends AppCompatActivity {
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_progress.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/underconstruction/views/UnderConstructionView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.underconstruction.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | public interface UnderConstructionView extends AppView {
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/home/views/HomeView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.home.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | /**
6 | * Created by weili on 12/09/2017.
7 | */
8 |
9 | public interface HomeView extends AppView {
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/device/views/DeviceView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.device.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 |
5 | /**
6 | * Created by tjackman on 16/10/17.
7 | */
8 |
9 | public interface DeviceView extends AppView {
10 | }
11 |
--------------------------------------------------------------------------------
/app/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/pushToKryptowire.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$#" -ne 1 ]; then
4 | echo "Usage: $0 API_KEY" >&2
5 | exit 1
6 | fi
7 |
8 | KRYPTOWIRE_API_KEY=$1
9 | URL="https://appsrv2.kryptowire.com/api/analyze"
10 | PLATFORM="android"
11 | FILEPATH="./app/build/outputs/apk/app-release.apk"
12 |
13 | curl -X POST $URL -F file=@$FILEPATH -F key=$KRYPTOWIRE_API_KEY -F platform=$PLATFORM
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/menu_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/landing/views/LandingViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.landing.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | public abstract class LandingViewImpl extends BaseAppView implements LandingView {
8 | public LandingViewImpl(Fragment fragment) {
9 | super(fragment);
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_person.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/callbacks/CallbackHandler.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.callbacks;
2 |
3 | /**
4 | * Represents the callback functions to be invoken when async functions are finished.
5 | */
6 |
7 | public interface CallbackHandler {
8 | default void onSuccess() {}
9 | default void onSuccess(T models) {onSuccess();}
10 | void onError(Throwable error);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_config.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_storage.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/documentation/views/DocumentationViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.documentation.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | public class DocumentationViewImpl extends BaseAppView implements DocumentationView {
8 | public DocumentationViewImpl (Fragment fragment) {
9 | super(fragment);
10 | }
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/push/views/PushViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.push.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | /**
8 | * Created by tjackman on 02/05/18.
9 | */
10 |
11 | public abstract class PushViewImpl extends BaseAppView implements PushView {
12 | public PushViewImpl(Fragment fragment) {
13 | super(fragment);
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_device_os.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 | Add a brief and meaningful description.
3 |
4 | ## Expected Behavior
5 | Describe the expected behaviour.
6 |
7 | ## Actual Behavior
8 | Describe the current/actual behaviour.
9 |
10 | ## Environment
11 | - Core Version:
12 | - Auth Version:
13 | - Push Version:
14 |
15 | ## Steps to reproduce
16 | Describe all steps and pre-requirements which are required to be performed in order to reproduce this scenario. ( E.g 1. Action, 2. Action ... )
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/device/views/DeviceViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.device.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | /**
8 | * Created by tjackman on 16/10/17.
9 | */
10 |
11 | public class DeviceViewImpl extends BaseAppView implements DeviceView {
12 | public DeviceViewImpl(Fragment fragment) {
13 | super(fragment);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/home/views/HomeViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.home.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | /**
8 | * Created by weili on 12/09/2017.
9 | */
10 |
11 | public abstract class HomeViewImpl extends BaseAppView implements HomeView {
12 | public HomeViewImpl(Fragment fragment) {
13 | super(fragment);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/landing/presenters/LandingPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.landing.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.push.views.PushView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | public class LandingPresenter extends BasePresenter {
9 |
10 | @Inject
11 | public LandingPresenter() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/underconstruction/views/UnderConstructionViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.underconstruction.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | public abstract class UnderConstructionViewImpl extends BaseAppView implements UnderConstructionView {
8 | public UnderConstructionViewImpl(Fragment fragment) {
9 | super(fragment);
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_security.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_message.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_insert_chart.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_note_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/documentation/presenters/DocumentationPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.documentation.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.documentation.views.DocumentationView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | public class DocumentationPresenter extends BasePresenter {
9 | @Inject
10 | public DocumentationPresenter() {
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/aerogear/androidshowcase/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/views/AuthenticationView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.views;
2 | import com.aerogear.androidshowcase.mvp.views.AppView;
3 | import org.aerogear.mobile.auth.user.UserPrincipal;
4 |
5 |
6 | /**
7 | * Created by weili on 12/09/2017.
8 | */
9 |
10 | public interface AuthenticationView extends AppView {
11 |
12 | public void renderIdentityInfo(UserPrincipal user);
13 |
14 | public void showAuthError(Exception error);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/home/presenters/HomeViewPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.home.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.home.views.HomeView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | /**
9 | * Created by weili on 12/09/2017.
10 | */
11 |
12 | public class HomeViewPresenter extends BasePresenter {
13 | @Inject
14 | public HomeViewPresenter() {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/views/AuthenticationDetailsView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.views;
2 |
3 | import com.aerogear.androidshowcase.mvp.views.AppView;
4 | import org.aerogear.mobile.auth.user.UserPrincipal;
5 |
6 | /**
7 | * Created by weili on 12/09/2017.
8 | */
9 |
10 | public interface AuthenticationDetailsView extends AppView {
11 |
12 | public void logoutSuccess(UserPrincipal user);
13 |
14 | public void logoutFailure(Exception error);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/views/AuthenticationViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | /**
8 | * Created by weili on 12/09/2017.
9 | */
10 |
11 | public abstract class AuthenticationViewImpl extends BaseAppView implements AuthenticationView {
12 | public AuthenticationViewImpl(Fragment fragment) {
13 | super(fragment);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/push/presenters/PushPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.push.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.push.views.PushView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | /**
9 | * Created by tjackman on 02/05/18.
10 | */
11 |
12 | public class PushPresenter extends BasePresenter {
13 |
14 | @Inject
15 | public PushPresenter() {
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/underconstruction/presenters/UnderConstructionPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.underconstruction.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.push.views.PushView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | public class UnderConstructionPresenter extends BasePresenter {
9 |
10 | @Inject
11 | public UnderConstructionPresenter() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | AeroGear Android Showcase
4 | Project mobile-security-android-template created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/device/presenters/DevicePresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.device.presenters;
2 |
3 | import com.aerogear.androidshowcase.features.device.views.DeviceView;
4 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
5 |
6 | import javax.inject.Inject;
7 |
8 | /**
9 | * Created by tjackman on 16/10/17.
10 | */
11 |
12 | public class DevicePresenter extends BasePresenter {
13 |
14 | @Inject
15 | public DevicePresenter() {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/di/MainActivityModule.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import com.aerogear.androidshowcase.MainActivity;
4 | import dagger.Module;
5 | import dagger.android.ContributesAndroidInjector;
6 |
7 | /**
8 | * Define dependencies for the MainActivity. Used by Dagger2 to build dependency graph.
9 | */
10 | @Module
11 | public abstract class MainActivityModule {
12 |
13 | @ContributesAndroidInjector(modules = {FragmentModules.class})
14 | abstract MainActivity contributeMainActivityInjector();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/views/AuthenticationDetailsViewImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.views;
2 |
3 | import android.app.Fragment;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.BaseAppView;
6 |
7 | /**
8 | * Created by weili on 12/09/2017.
9 | */
10 |
11 | public abstract class AuthenticationDetailsViewImpl extends BaseAppView implements AuthenticationDetailsView {
12 | public AuthenticationDetailsViewImpl(Fragment fragment) {
13 | super(fragment);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_crypto.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_memory.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/profile_image_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_account_circle.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_http.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_not_available_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2e6980
4 | #4296b4
5 | #23242d
6 | #2e6980
7 | #000000
8 | #D82B32
9 | #008000
10 | #e25027
11 | #fff
12 | #8e8e8e
13 | #f4f4f4
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_lock_open.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/configurations/ApiServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.configurations;
2 |
3 | /**
4 | * Created by weili on 31/10/2017.
5 | */
6 |
7 | public class ApiServerConfiguration {
8 |
9 | private String apiServerUrl;
10 |
11 | ApiServerConfiguration(String apiServerUrl) {
12 | this.apiServerUrl = apiServerUrl;
13 | }
14 |
15 | public String getServerUrl() {
16 | return this.apiServerUrl;
17 | }
18 |
19 | public String getNoteAPIUrl() {
20 | return String.format("%s/note", getServerUrl());
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_http.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_hooking.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_network_locked.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | app
4 | Project app created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_phone_lock.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_warning_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/di/SecureTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import com.aerogear.androidshowcase.SecureApplication;
4 |
5 | /**
6 | * Setup the DI for the tests. This class will initialise the dagger component for the application.
7 | */
8 |
9 | public class SecureTestApplication extends SecureApplication {
10 |
11 | SecureApplicationTestComponent component;
12 |
13 | @Override
14 | protected void initInjector() {
15 | component = DaggerSecureApplicationTestComponent.builder().application(this).build();
16 | component.inject(this);
17 | }
18 |
19 | public SecureApplicationTestComponent getComponent() {
20 | return component;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_not_available_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/push_registration_failed_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/models/User.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.models;
2 |
3 | /**
4 | * Created by tjackman on 01/05/18.
5 | */
6 |
7 | public class User {
8 | private Long id;
9 | private String name;
10 | private String email;
11 |
12 | public Long getId() {
13 | return id;
14 | }
15 |
16 | public void setId(Long id) {
17 | this.id = id;
18 | }
19 |
20 | public String getName() {
21 | return name;
22 | }
23 |
24 | public void setName(String name) {
25 | this.name = name;
26 | }
27 |
28 | public String getEmail() {
29 | return email;
30 | }
31 |
32 | public void setEmail(String email) {
33 | this.email = email;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notifications_active.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/MockTestRunner.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.support.test.runner.AndroidJUnitRunner;
6 |
7 | import com.aerogear.androidshowcase.di.SecureTestApplication;
8 |
9 | /**
10 | * Define the test runner. Override the default Android one as we need use the application class defined by ourselves to setup DI.
11 | */
12 |
13 | public class MockTestRunner extends AndroidJUnitRunner {
14 | @Override
15 | public Application newApplication(ClassLoader cl, String className, Context context)
16 | throws InstantiationException, IllegalAccessException, ClassNotFoundException {
17 | return super.newApplication(cl, SecureTestApplication.class.getName(), context);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/providers/OpenIDAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.providers;
2 | import android.app.Activity;
3 |
4 | import org.aerogear.mobile.core.Callback;
5 |
6 |
7 | /**
8 | * An interface for OpenID Connect Authentication Providers
9 | */
10 | public interface OpenIDAuthenticationProvider {
11 |
12 | /**
13 | * Perform a login to the OIDC server
14 | * @param fromActivity - the activity to use for authenticating
15 | * @param authCallback - the authentication callback
16 | */
17 | void login(Activity fromActivity, Callback authCallback);
18 |
19 | /**
20 | * Perform a logout against the OIDC server
21 | *
22 | * @param logoutCallback the logout callback
23 | */
24 | void logout(Callback logoutCallback);
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/fh_icon.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_push.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/presenters/BasePresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.presenters;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.aerogear.androidshowcase.mvp.views.AppView;
6 |
7 | /**
8 | * A base implementation for the presenter interface
9 | */
10 |
11 | public class BasePresenter implements Presenter {
12 | protected VIEW view;
13 |
14 | @Override
15 | public void attachView(@NonNull VIEW view) {
16 | this.view = view;
17 | this.onViewAttached();
18 | }
19 |
20 | @Override
21 | public void detachView() {
22 | this.view = null;
23 | this.onViewDetached();
24 | }
25 |
26 | @Override
27 | public void resume() {
28 |
29 | }
30 |
31 | @Override
32 | public void pause() {
33 |
34 | }
35 |
36 | protected void onViewAttached() {
37 |
38 | }
39 |
40 | protected void onViewDetached() {
41 |
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_app_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/configurations/AppConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.configurations;
2 |
3 | import org.aerogear.mobile.core.MobileCore;
4 | import org.aerogear.mobile.core.configuration.ServiceConfiguration;
5 |
6 | import javax.inject.Inject;
7 | import javax.inject.Singleton;
8 |
9 | /**
10 | * Created by weili on 31/10/2017.
11 | */
12 | @Singleton
13 | public class AppConfiguration {
14 |
15 | private static final String NOTES_SERVER_KEY = "notes-service";
16 |
17 | private ApiServerConfiguration apiServerConfiguration;
18 |
19 | @Inject
20 | public AppConfiguration() {
21 | readConfigurations();
22 | }
23 |
24 | private void readConfigurations() {
25 | final ServiceConfiguration serviceConfiguration = MobileCore.getInstance().getServiceConfigurationById(NOTES_SERVER_KEY);
26 | final String serviceUrl = serviceConfiguration.getUrl();
27 | this.apiServerConfiguration = new ApiServerConfiguration(serviceUrl);
28 | }
29 |
30 | public ApiServerConfiguration getAPIServerConfiguration() {
31 | return this.apiServerConfiguration;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/presenters/Presenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.presenters;
2 |
3 |
4 | import com.aerogear.androidshowcase.mvp.views.AppView;
5 |
6 | /**
7 | * Interface representing a Presenter in a model-view-presenter (MVP) pattern.
8 | * In most cases, the presenter is bound with a view. Given that the view will have its life cycles, the presenter needs to be aware of the view's life cycle.
9 | */
10 |
11 | public interface Presenter {
12 |
13 | /**
14 | * Attach the view to the presenter
15 | * @param view the view to attach
16 | */
17 | void attachView(VIEW view);
18 |
19 | /**
20 | * detach the view from the presenter
21 | */
22 | void detachView();
23 |
24 | /**
25 | * Method that control the lifecycle of the view. It should be called in the view's
26 | * (Activity or Fragment) onResume() method.
27 | */
28 | void resume();
29 |
30 | /**
31 | * Method that control the lifecycle of the view. It should be called in the view's
32 | * (Activity or Fragment) onPause() method.
33 | */
34 | void pause();
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/views/AppView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.views;
2 |
3 | /**
4 | * Interface to represent a View that will load data.
5 | * The data loading should happen in a backgroun thread.
6 | * The View will show/hide the progress indicator accordingly.
7 | */
8 |
9 | public interface AppView {
10 |
11 | /**
12 | * Show the progress bar to indicate the loading is in progress
13 | */
14 | void showLoading();
15 |
16 | /**
17 | * Hide the progress bar
18 | */
19 | void hideLoading();
20 |
21 | /**
22 | * Show a message using snackbar
23 | * @param message the message to show
24 | */
25 | void showMessage(String message);
26 |
27 | /**
28 | * Show a message using snackbar
29 | * @param messageResourceId the resource id of the message
30 | */
31 | void showMessage(int messageResourceId);
32 |
33 | /**
34 | * Show a message using snackbar
35 | * @param messageResourceId the resource id of the message
36 | * @param formatArgs format arguments
37 | */
38 | void showMessage(int messageResourceId, Object... formatArgs);
39 | }
40 |
--------------------------------------------------------------------------------
/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 /home/tjackman/Android/Sdk/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 | -dontwarn com.google.errorprone.annotations.*
27 | -dontwarn okio.**
28 | -dontwarn org.slf4j.**
29 |
30 | -keep class net.sqlcipher.** {
31 | *;
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/documentation/DocumentUrl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.documentation;
2 |
3 | /**
4 | * This class enumerates the documentation pages we have.
5 | */
6 | public enum DocumentUrl {
7 | PUSH("https://docs.aerogear.org/external/showcase/android/push.html"),
8 | IDENTITY_MANAGEMENT("https://docs.aerogear.org/external/showcase/android/idm.html"),
9 | DEVICE_SECURITY("https://docs.aerogear.org/external/showcase/android/security.html"),
10 | METRICS("https://docs.aerogear.org/external/showcase/android/metrics.html"),
11 | RUNTIME("https://docs.aerogear.org/external/showcase/android/runtime.html"),
12 | IDENTITY_MANAGEMENT_SSO("https://docs.aerogear.org/external/showcase/android/idmsso.html"),
13 | NOTES_SERVICE("https://github.com/feedhenry/mobile-security/tree/master/projects/api-server"),
14 | SELF_SIGNED_DOCS("https://docs.aerogear.org/external/cert/self-signed-cert.html");
15 |
16 | private final String url;
17 |
18 | DocumentUrl(String urlString) {
19 | this.url = urlString;
20 |
21 | }
22 |
23 | public String getUrl() {
24 | return url;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/push/PushGestureViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.push;
2 |
3 | import android.support.annotation.Nullable;
4 | import android.view.View;
5 | import android.widget.TextView;
6 |
7 | import com.aerogear.androidshowcase.R;
8 | import com.thesurix.gesturerecycler.GestureViewHolder;
9 |
10 | import butterknife.BindView;
11 | import butterknife.ButterKnife;
12 |
13 | public class PushGestureViewHolder extends GestureViewHolder {
14 |
15 | @Nullable
16 | @BindView(R.id.foreground_view)
17 | View mForegroundView;
18 |
19 | @BindView(R.id.message)
20 | TextView message;
21 |
22 | @BindView(R.id.receivedAt)
23 | TextView receivedAt;
24 |
25 |
26 | public PushGestureViewHolder(View itemView) {
27 | super(itemView);
28 |
29 | ButterKnife.bind(this, itemView);
30 | }
31 |
32 | @Override
33 | public boolean canDrag() {
34 | return false;
35 | }
36 |
37 | @Override
38 | public boolean canSwipe() {
39 | return true;
40 | }
41 |
42 | @Override
43 | public View getForegroundView() {
44 | return mForegroundView;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/utils/StreamUtils.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.utils;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 |
7 | /**
8 | * Some utility functions to perform IO stream related operations.
9 | */
10 |
11 | public class StreamUtils {
12 |
13 | /**
14 | * Read UTF8 string from the given input steam.
15 | * @param in the input stream
16 | * @return the string value from the input stream
17 | * @throws IOException
18 | */
19 | public static String readStream(InputStream in) throws IOException {
20 | byte[] bytes = readStreamBytes(in);
21 | String content = new String(bytes, "utf-8");
22 | return content;
23 | }
24 |
25 | public static byte[] readStreamBytes(InputStream in) throws IOException {
26 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
27 | byte[] b = new byte[1024];
28 | int numberOfBytesRead;
29 | while ((numberOfBytesRead = in.read(b)) >= 0) {
30 | baos.write(b, 0, numberOfBytesRead);
31 | }
32 | return baos.toByteArray();
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/push/PushGestureAdapter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.push;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import com.aerogear.androidshowcase.R;
9 | import com.thesurix.gesturerecycler.GestureAdapter;
10 |
11 | public class PushGestureAdapter
12 | extends GestureAdapter {
13 |
14 | @NonNull
15 | @Override
16 | public PushGestureViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
17 |
18 | View view = LayoutInflater
19 | .from(parent.getContext())
20 | .inflate(R.layout.item_push, parent, false);
21 |
22 | return new PushGestureViewHolder(view);
23 |
24 | }
25 |
26 | @Override
27 | public void onBindViewHolder(@NonNull PushGestureViewHolder holder, int position) {
28 | PushFragment.PushMessage pushMessage = getItem(position);
29 |
30 | holder.message.setText(pushMessage.getMessage());
31 | holder.receivedAt.setText(pushMessage.getDateReceived());
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
17 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_notes_list.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/di/SecureApplicationComponent.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.aerogear.androidshowcase.SecureApplication;
6 | import com.aerogear.androidshowcase.domain.crypto.AesCrypto;
7 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
8 | import dagger.BindsInstance;
9 | import dagger.Component;
10 | import dagger.android.AndroidInjectionModule;
11 | import javax.inject.Singleton;
12 |
13 | /**
14 | * Define the dependencies of the app. Used by Dagger2 to build dependency graph.
15 | */
16 | @Singleton
17 | @Component(modules = {AndroidInjectionModule.class, SecureApplicationModule.class,
18 | MainActivityModule.class})
19 | public interface SecureApplicationComponent {
20 |
21 | @Component.Builder
22 | interface Builder {
23 |
24 | @BindsInstance
25 | Builder application(Application application);
26 |
27 | SecureApplicationComponent build();
28 | }
29 |
30 | void inject(SecureApplication app);
31 |
32 | Context context();
33 |
34 | AesCrypto provideAesCrypto();
35 |
36 | OpenIDAuthenticationProvider authProvider();
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_area.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Motivation
2 | Add references to relevant tickets or a short description about what motivated you do it. (E.g JIRA: https://issues.jboss.org/browse/AEROGEAR-{} AND/OR ISSUE: https://github.com/aerogear/standards/issues/{})
3 |
4 | ## What
5 | Add an short answer for: What was done in this PR? (E.g Don't allow users has access to the feature X.)
6 |
7 | ## Why
8 | Add an short answer for: Why it was done? (E.g The feature X was deprecated.)
9 |
10 | ## How
11 | Add an short answer for: How it was done? (E.g By removing this feature from ... OR By removing just the button but not its implementation ... )
12 |
13 | ## Verification Steps
14 | Add the steps required to check this change. Following an example.
15 |
16 | 1. Go to `XX >> YY >> SS`
17 | 2. Create a new item `N` with the info `X`
18 | 3. Try to edit this item
19 | 4. Check if in the left menu the feature X is not so long present.
20 |
21 | ## Checklist:
22 |
23 | - [ ] Code has been tested locally by PR requester
24 | - [ ] Changes have been successfully verified by another team member
25 |
26 | ## Progress
27 |
28 | - [x] Finished task
29 | - [ ] TODO
30 |
31 | ## Additional Notes
32 |
33 | PS.: Add images and/or .gifs to illustrate what was changed if this pull request modifies the appearance/output of something presented to the users.
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/SecureKeyStoreImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import java.io.IOException;
4 | import java.security.GeneralSecurityException;
5 | import java.security.KeyStore;
6 |
7 | /**
8 | * Base class that implements the SecureKeystore interface.
9 | */
10 |
11 | public abstract class SecureKeyStoreImpl implements SecureKeyStore {
12 |
13 | protected static final String ANDROID_KEY_STORE = "AndroidKeyStore";
14 |
15 | protected static final int AES_KEYSIZE_128 = 128;
16 | protected static final int RSA_KEY_SIZE = 2048;
17 |
18 | protected static final String ALG_AES_GCM_NOPADDING = "AES/GCM/NoPadding";
19 | protected static final String ALG_RSA_ECB_PCKS1Padding = "RSA/ECB/PKCS1Padding";
20 | protected static final String ALG_RSA_ECB_OAEPPadding = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
21 |
22 | protected KeyStore loadKeyStore() throws GeneralSecurityException, IOException {
23 | KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
24 | keyStore.load(null);
25 | return keyStore;
26 | }
27 |
28 | @Override
29 | public KeyStore.Entry getKeyPairEntry(String keyAlias) throws GeneralSecurityException, IOException {
30 | KeyStore ks = loadKeyStore();
31 | return ks.getEntry(keyAlias, null);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "56938872708",
4 | "firebase_url": "https://aerogear-playground.firebaseio.com",
5 | "project_id": "aerogear-playground",
6 | "storage_bucket": "aerogear-playground.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:56938872708:android:1511d73e1e8c02f1",
12 | "android_client_info": {
13 | "package_name": "com.aerogear.androidshowcase"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "56938872708-guthigb9rlncora778hs22ufbkqpjtom.apps.googleusercontent.com",
19 | "client_type": 3
20 | },
21 | {
22 | "client_id": "56938872708-7f2mrm3edeu8g6iiio78nc96kk7mohet.apps.googleusercontent.com",
23 | "client_type": 3
24 | }
25 | ],
26 | "api_key": [
27 | {
28 | "current_key": "AIzaSyAAM11RlZfu76uhYcM7QDmxNNS9u9xl4z4"
29 | }
30 | ],
31 | "services": {
32 | "analytics_service": {
33 | "status": 1
34 | },
35 | "appinvite_service": {
36 | "status": 1,
37 | "other_platform_oauth_client": []
38 | },
39 | "ads_service": {
40 | "status": 2
41 | }
42 | }
43 | }
44 | ],
45 | "configuration_version": "1"
46 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/Constants.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Created by weili on 13/09/2017.
7 | */
8 |
9 | public class Constants {
10 |
11 | public static final class REQUEST_CODES {
12 | public static final int AUTH_CODE = 1;
13 | }
14 |
15 | public static final class TOKEN_FIELDS {
16 | public static final String AUTH_STATE = "authState";
17 | public static final String IDENTITY_DATA = "identityData";
18 | }
19 |
20 |
21 | public static final class OPEN_ID_CONNECT_CONFIG {
22 | public static final Uri REDIRECT_URI = Uri.parse("com.aerogear.androidshowcase:/callback");
23 | public static final String OPEN_ID_SCOPE = "openid";
24 | }
25 |
26 | public static final class ACCESS_CONTROL_ROLES {
27 | public static final String ROLE_MOBILE_USER = "mobile-user";
28 | public static final String ROLE_API_ACCESS = "api-access";
29 | public static final String ROLE_SUPERUSER = "superuser";
30 | }
31 |
32 | public static final class NOTE_FIELDS {
33 | public static final String ID_FIELD = "id";
34 | public static final String TITLE_FIELD = "title";
35 | public static final String CONTENT_FIELD = "content";
36 | public static final String STORE_TYPE_FIELD = "storeType";
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/RsaCryptoTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.support.test.InstrumentationRegistry;
4 |
5 | import com.aerogear.androidshowcase.di.SecureTestApplication;
6 | import com.aerogear.androidshowcase.domain.crypto.RsaCrypto;
7 |
8 | import org.junit.Before;
9 | import org.junit.Test;
10 |
11 | import java.io.IOException;
12 | import java.security.GeneralSecurityException;
13 |
14 | import javax.inject.Inject;
15 |
16 | import static junit.framework.Assert.assertEquals;
17 |
18 | /**
19 | * Created by weili on 27/09/2017.
20 | */
21 |
22 | public class RsaCryptoTest {
23 | @Inject
24 | RsaCrypto rsaCrytoTest;
25 |
26 | @Before
27 | public void setUp() {
28 | SecureTestApplication application = (SecureTestApplication) InstrumentationRegistry.getTargetContext().getApplicationContext();
29 | application.getComponent().inject(this);
30 | }
31 |
32 | @Test
33 | public void testCryptoOperations() throws GeneralSecurityException, IOException {
34 | String testKeyAlias = "RSACryptoTestKey";
35 | String textToEncrypt = "this is a test text";
36 | byte[] encrypted = rsaCrytoTest.encrypt(testKeyAlias,textToEncrypt.getBytes("utf-8"));
37 | byte[] decrypted = rsaCrytoTest.decrypt(testKeyAlias, encrypted);
38 | assertEquals(textToEncrypt, new String(decrypted, "utf-8"));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/domain/store/sqlite/SqliteNoteStoreTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.store.sqlite;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 |
6 | import com.aerogear.androidshowcase.di.SecureTestApplication;
7 | import com.aerogear.androidshowcase.domain.store.NoteStoreTestBase;
8 |
9 | import org.junit.After;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | import javax.inject.Inject;
14 |
15 | import static junit.framework.Assert.assertEquals;
16 | import static junit.framework.Assert.assertNotNull;
17 |
18 | public class SqliteNoteStoreTest extends NoteStoreTestBase {
19 |
20 | @Inject
21 | Context context;
22 |
23 | @Inject
24 | SqliteNoteStore sqliteStore;
25 |
26 | @Before
27 | public void setup() {
28 | SecureTestApplication application = (SecureTestApplication) InstrumentationRegistry.getTargetContext().getApplicationContext();
29 | application.getComponent().inject(this);
30 | cleardb();
31 | }
32 |
33 | @After
34 | public void teardown() {
35 | cleardb();
36 | }
37 |
38 | @Test
39 | public void testSqliteNoteStore() throws Exception {
40 | noteCRUDL(this.sqliteStore);
41 | }
42 |
43 | private void cleardb() {
44 | this.context.deleteDatabase(NoteDbHelper.DATABASE_NAME);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_error_background.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
17 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/presenters/AuthenticationDetailsPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.presenters;
2 |
3 | import com.aerogear.androidshowcase.MainActivity;
4 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
5 | import com.aerogear.androidshowcase.features.authentication.views.AuthenticationDetailsView;
6 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
7 | import org.aerogear.mobile.auth.user.UserPrincipal;
8 | import org.aerogear.mobile.core.Callback;
9 |
10 | import javax.inject.Inject;
11 |
12 | /**
13 | * Created by weili on 12/09/2017.
14 | */
15 |
16 | public class AuthenticationDetailsPresenter extends BasePresenter {
17 |
18 | OpenIDAuthenticationProvider authProvider;
19 | MainActivity mainActivity;
20 |
21 | @Inject
22 | public AuthenticationDetailsPresenter(OpenIDAuthenticationProvider authProviderImpl, MainActivity mainActivity) {
23 | this.authProvider = authProviderImpl;
24 | this.mainActivity = mainActivity;
25 | }
26 |
27 | public void logout(){
28 | authProvider.logout(new Callback() {
29 | @Override
30 | public void onSuccess(UserPrincipal user) {
31 | view.logoutSuccess(user);
32 | }
33 |
34 | @Override
35 | public void onError(Throwable error) {
36 | view.logoutFailure((Exception) error);
37 | }
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/presenters/AuthenticationViewPresenter.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.presenters;
2 |
3 | import com.aerogear.androidshowcase.MainActivity;
4 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
5 | import com.aerogear.androidshowcase.features.authentication.views.AuthenticationView;
6 | import com.aerogear.androidshowcase.mvp.presenters.BasePresenter;
7 |
8 | import org.aerogear.mobile.auth.user.UserPrincipal;
9 | import org.aerogear.mobile.core.Callback;
10 |
11 | import javax.inject.Inject;
12 |
13 | /**
14 | * Created by weili on 12/09/2017.
15 | */
16 |
17 | public class AuthenticationViewPresenter extends BasePresenter {
18 |
19 | OpenIDAuthenticationProvider authProvider;
20 | MainActivity mainActivity;
21 |
22 | @Inject
23 | public AuthenticationViewPresenter(OpenIDAuthenticationProvider authProviderImpl, MainActivity mainActivity) {
24 | this.authProvider = authProviderImpl;
25 | this.mainActivity = mainActivity;
26 | }
27 |
28 | public void doLogin() {
29 | authProvider.login(mainActivity, new Callback() {
30 | @Override
31 | public void onSuccess(UserPrincipal user) {
32 | view.renderIdentityInfo(user);
33 | }
34 |
35 | @Override
36 | public void onError(Throwable error) {
37 | view.showAuthError((Exception) error);
38 | }
39 | });
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/domain/store/SecureFileNoteStoreTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.store;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 |
6 | import com.aerogear.androidshowcase.di.SecureTestApplication;
7 |
8 | import org.junit.After;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | import java.io.File;
13 |
14 | import javax.inject.Inject;
15 |
16 | /**
17 | * Created by weili on 25/09/2017.
18 | */
19 |
20 | public class SecureFileNoteStoreTest extends NoteStoreTestBase {
21 | @Inject
22 | Context context;
23 |
24 | @Inject
25 | SecureFileNoteStore secureFileNoteStore;
26 |
27 | @Before
28 | public void setup() {
29 | SecureTestApplication application = (SecureTestApplication) InstrumentationRegistry.getTargetContext().getApplicationContext();
30 | application.getComponent().inject(this);
31 | removeFiles(this.context);
32 | }
33 |
34 | @After
35 | public void teardown() {
36 | removeFiles(this.context);
37 | }
38 |
39 | @Test
40 | public void testSecureFileNoteStore() throws Exception {
41 | noteCRUDL(this.secureFileNoteStore);
42 | }
43 |
44 | public static void removeFiles(Context context) {
45 | File testDir = context.getFilesDir();
46 | if (testDir.exists()) {
47 | File[] files = testDir.listFiles();
48 | for (File f : files) {
49 | f.delete();
50 | }
51 | testDir.delete();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/RsaCrypto.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import java.io.IOException;
4 | import java.security.GeneralSecurityException;
5 | import java.security.KeyStore;
6 |
7 | import javax.inject.Inject;
8 |
9 | /**
10 | * Perform data encryption/decryption using RSA key pairs. It can be used to encrypt/decrypt small amount of data.
11 | */
12 | public class RsaCrypto {
13 |
14 |
15 | SecureKeyStore secureKeyStore;
16 |
17 | @Inject
18 | public RsaCrypto(SecureKeyStore secureKeyStore) {
19 | this.secureKeyStore = secureKeyStore;
20 | }
21 |
22 | public byte[] encrypt(String keyAlias, byte[] textToEncrypt) throws GeneralSecurityException, IOException {
23 | if (!secureKeyStore.hasKeyPair(keyAlias)) {
24 | secureKeyStore.generatePrivateKeyPair(keyAlias);
25 | }
26 | KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) secureKeyStore.getKeyPairEntry(keyAlias);
27 | return RsaHelper.encrypt(secureKeyStore.getSupportedRSAMode(), privateKeyEntry, textToEncrypt);
28 | }
29 |
30 | public byte[] decrypt(String keyAlias, byte[] dataToDecrypt) throws GeneralSecurityException, IOException {
31 | if (!secureKeyStore.hasKeyPair(keyAlias)) {
32 | throw new GeneralSecurityException("missing key " + keyAlias);
33 | }
34 | KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) secureKeyStore.getKeyPairEntry(keyAlias);
35 | return RsaHelper.decrypt(secureKeyStore.getSupportedRSAMode(), privateKeyEntry, dataToDecrypt);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/components/ProgressDialogHelper.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.components;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Context;
5 |
6 | /**
7 | * A helper class to hide/show the progress dialog
8 | */
9 |
10 | public class ProgressDialogHelper {
11 | private ProgressDialog dialog;
12 |
13 | private volatile int progressesCount = 0;
14 |
15 | public void showProgress(Context context) {
16 | showProgress(context, null);
17 | }
18 |
19 | public void showProgress(Context context, String message) {
20 | showProgress(context, message, null);
21 | }
22 |
23 | public void showProgress(Context context, String message, String title) {
24 | if (context == null) {
25 | return;
26 | }
27 |
28 | if (!inProgress()) {
29 | dialog = new ProgressDialog(context);
30 | if (message != null) dialog.setMessage(message);
31 | if (title != null) dialog.setTitle(title);
32 | dialog.setIndeterminate(true);
33 | dialog.setCanceledOnTouchOutside(false);
34 | dialog.show();
35 | }
36 |
37 | progressesCount++;
38 | }
39 |
40 | public void hideProgress() {
41 | progressesCount--;
42 | if (progressesCount <= 0) {
43 | if (dialog != null && dialog.isShowing()) {
44 | dialog.dismiss();
45 | }
46 | progressesCount = 0;
47 | }
48 |
49 | }
50 |
51 | private boolean inProgress() {
52 | return dialog != null && dialog.isShowing();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_landing.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
15 |
16 |
25 |
26 |
27 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_note.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
18 |
19 |
28 |
29 |
30 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/views/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.views;
2 |
3 | import android.app.Fragment;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.view.View;
7 |
8 | import com.aerogear.androidshowcase.mvp.presenters.Presenter;
9 |
10 | /**
11 | * Base fragment. It implements the methods to make sure the presenter and view are initialised correctly, make sure the presenter will get notified about various life cycle events of the fragment.
12 | */
13 |
14 | public abstract class BaseFragment, VIEW extends AppView> extends Fragment {
15 | protected PRESENTER presenter;
16 |
17 | protected VIEW view;
18 |
19 | @Override
20 | public void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | presenter = initPresenter();
23 | view = initView();
24 | }
25 |
26 | @Override
27 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
28 | super.onViewCreated(view, savedInstanceState);
29 | presenter.attachView(this.view);
30 | }
31 |
32 | @Override
33 | public void onResume() {
34 | super.onResume();
35 | presenter.resume();
36 | }
37 |
38 | @Override
39 | public void onPause() {
40 | super.onPause();
41 | presenter.pause();
42 | }
43 |
44 | @Override
45 | public void onDestroyView() {
46 | super.onDestroyView();
47 | presenter.detachView();
48 | }
49 |
50 | protected abstract PRESENTER initPresenter();
51 |
52 | protected abstract VIEW initView();
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: ~/code
5 | docker:
6 | - image: circleci/android:api-27-alpha
7 | environment:
8 | JVM_OPTS: -Xmx3200m
9 | steps:
10 | - checkout
11 | - restore_cache:
12 | key: jars-{{ checksum "build.gradle" }}
13 | - run:
14 | name: Chmod permissions
15 | command: sudo chmod +x ./gradlew
16 | - save_cache:
17 | paths:
18 | - ~/.gradle
19 | key: jars-{{ checksum "build.gradle" }}
20 | - run:
21 | name: Run Tests
22 | command: ./gradlew compileReleaseSources
23 |
24 | pushToKryptoWire:
25 | working_directory: ~/code
26 | docker:
27 | - image: circleci/android:api-27-alpha
28 | environment:
29 | JVM_OPTS: -Xmx3200m
30 | steps:
31 | - checkout
32 | - restore_cache:
33 | key: jars-{{ checksum "build.gradle" }}
34 | - run:
35 | name: Chmod permissions
36 | command: sudo chmod +x ./gradlew
37 | - save_cache:
38 | paths:
39 | - ~/.gradle
40 | key: jars-{{ checksum "build.gradle" }}
41 | - run:
42 | name: Build Release
43 | command: ./gradlew aR
44 | - run:
45 | name: Push To Kryptowire
46 | command: curl -X POST "https://emm.kryptowire.com/api/submit" -F app=@./app/build/outputs/apk/release/app-release.apk -F key=$KRYPTOWIRE_API_KEY -F platform=android
47 |
48 | workflows:
49 | version: 2
50 | build-and-push:
51 | jobs:
52 | - build
53 | - pushToKryptoWire:
54 | filters:
55 | branches:
56 | only:
57 | - master
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
36 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/SecureApplication.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.databinding.ObservableArrayList;
6 | import android.databinding.ObservableList;
7 |
8 | import com.aerogear.androidshowcase.di.DaggerSecureApplicationComponent;
9 | import com.aerogear.androidshowcase.features.push.PushFragment.PushMessage;
10 |
11 | import net.sqlcipher.database.SQLiteDatabase;
12 |
13 | import javax.inject.Inject;
14 |
15 | import dagger.android.AndroidInjector;
16 | import dagger.android.DispatchingAndroidInjector;
17 | import dagger.android.HasActivityInjector;
18 |
19 | /**
20 | * The main application class. Needs to setup dependency injection.
21 | */
22 |
23 | public class SecureApplication extends Application implements HasActivityInjector {
24 |
25 | private ObservableList pushMessagesReceived = new ObservableArrayList<>();
26 |
27 | @Inject
28 | DispatchingAndroidInjector dispatchingAndroidInjector;
29 |
30 | @Override
31 | public void onCreate() {
32 | super.onCreate();
33 | initInjector();
34 |
35 | try {
36 | SQLiteDatabase.loadLibs(this);
37 | } catch (UnsatisfiedLinkError e) {
38 | //only thrown during tests, ignore it
39 | }
40 | }
41 |
42 | protected void initInjector() {
43 | DaggerSecureApplicationComponent
44 | .builder()
45 | .application(this)
46 | .build()
47 | .inject(this);
48 | }
49 |
50 | @Override
51 | public AndroidInjector activityInjector() {
52 | return dispatchingAndroidInjector;
53 | }
54 |
55 | public ObservableList getPushMessagesReceived() {
56 | return pushMessagesReceived;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/di/FragmentModules.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 |
4 | import com.aerogear.androidshowcase.features.authentication.AuthenticationDetailsFragment;
5 | import com.aerogear.androidshowcase.features.authentication.AuthenticationFragment;
6 | import com.aerogear.androidshowcase.features.device.DeviceFragment;
7 | import com.aerogear.androidshowcase.features.documentation.DocumentationFragment;
8 | import com.aerogear.androidshowcase.features.home.HomeFragment;
9 | import com.aerogear.androidshowcase.features.landing.LandingFragment;
10 | import com.aerogear.androidshowcase.features.push.PushFragment;
11 | import com.aerogear.androidshowcase.features.underconstruction.UnderConstructionFragment;
12 |
13 | import dagger.Module;
14 | import dagger.android.ContributesAndroidInjector;
15 |
16 | /**
17 | * Define dependencies used by the NotesListFragment. Used by Dagger2 to build dependency graph.
18 | */
19 |
20 | @Module
21 | public abstract class FragmentModules {
22 |
23 | @ContributesAndroidInjector
24 | abstract HomeFragment contributeHomeFragmentInjector();
25 |
26 | @ContributesAndroidInjector
27 | abstract AuthenticationFragment contributeAuthenticationFragmentInjector();
28 |
29 | @ContributesAndroidInjector
30 | abstract AuthenticationDetailsFragment contributeAuthenticationDetailsFragmentInjector();
31 |
32 | @ContributesAndroidInjector
33 | abstract DeviceFragment contributeDeviceFragmentInjector();
34 |
35 | @ContributesAndroidInjector
36 | abstract PushFragment contributePushFragmentInjector();
37 |
38 | @ContributesAndroidInjector
39 | abstract DocumentationFragment contributeDocumentationFragmentInjector();
40 |
41 | @ContributesAndroidInjector
42 | abstract UnderConstructionFragment contributeUnderConstructionFragmentInjector();
43 |
44 | @ContributesAndroidInjector
45 | abstract LandingFragment contributeLandingFragmentInjector();
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/home/HomeFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.home;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.app.Fragment;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import com.aerogear.androidshowcase.R;
12 | import com.aerogear.androidshowcase.features.home.presenters.HomeViewPresenter;
13 | import com.aerogear.androidshowcase.features.home.views.HomeView;
14 | import com.aerogear.androidshowcase.features.home.views.HomeViewImpl;
15 | import com.aerogear.androidshowcase.mvp.views.BaseFragment;
16 |
17 | import javax.inject.Inject;
18 |
19 | import dagger.android.AndroidInjection;
20 |
21 | /**
22 | * A simple {@link Fragment} subclass.
23 | */
24 | public class HomeFragment extends BaseFragment {
25 |
26 | public static final String TAG = "home";
27 |
28 | @Inject
29 | HomeViewPresenter homeViewPresenter;
30 |
31 | public HomeFragment() {
32 | // Required empty public constructor
33 | }
34 |
35 |
36 | @Override
37 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
38 | Bundle savedInstanceState) {
39 |
40 | // Inflate the layout for this fragment
41 | return inflater.inflate(R.layout.fragment_home, container, false);
42 | }
43 |
44 | @Override
45 | public void onAttach(Activity activity) {
46 | AndroidInjection.inject(this);
47 | super.onAttach(activity);
48 | }
49 |
50 | @Override
51 | public void onDetach() {
52 | super.onDetach();
53 | this.homeViewPresenter = null;
54 | }
55 |
56 | @Override
57 | protected HomeViewPresenter initPresenter() {
58 | return homeViewPresenter;
59 | }
60 |
61 | @Override
62 | protected HomeView initView() {
63 | return new HomeViewImpl(this) {};
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/MainActivityInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.support.test.rule.ActivityTestRule;
4 | import android.support.test.runner.AndroidJUnit4;
5 |
6 | import org.junit.Before;
7 | import org.junit.Rule;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static android.support.test.espresso.Espresso.onView;
12 | import static android.support.test.espresso.action.ViewActions.click;
13 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
14 | import static android.support.test.espresso.contrib.DrawerActions.open;
15 | import static android.support.test.espresso.contrib.DrawerMatchers.isClosed;
16 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
17 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
18 | import static android.support.test.espresso.matcher.ViewMatchers.withText;
19 |
20 | /**
21 | * Instrumentation test, which will execute on an Android device.
22 | *
23 | * @see Testing documentation
24 | */
25 | @RunWith(AndroidJUnit4.class)
26 | public class MainActivityInstrumentedTest {
27 |
28 | @Rule
29 | public ActivityTestRule activityRule = new ActivityTestRule(MainActivity.class);
30 |
31 | @Test
32 | public void homeViewDisplayed() throws Exception {
33 | onView(withId(R.id.home_title)).check(matches(isDisplayed()));
34 | onView(withId(R.id.drawer_layout)).check(matches(isClosed()));
35 | }
36 |
37 | @Test
38 | public void appNavigation() throws Exception {
39 | onView(withId(R.id.drawer_layout)).perform(open());
40 | navigateTo(R.string.fragment_title_authenticate, R.id.keycloakLogin);
41 | }
42 |
43 | private void navigateTo(final int titleId, final int fragmentId) throws Exception {
44 | onView(withText(titleId)).perform(click());
45 | onView(withId(fragmentId)).check(matches(isDisplayed()));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/providers/PushServiceProvider.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.providers;
2 |
3 | import com.aerogear.androidshowcase.R;
4 |
5 | import org.aerogear.mobile.core.MobileCore;
6 | import org.aerogear.mobile.core.executor.AppExecutors;
7 | import org.aerogear.mobile.core.reactive.Responder;
8 | import org.aerogear.mobile.push.PushService;
9 | import org.aerogear.mobile.push.UnifiedPushConfig;
10 |
11 | import java.util.Arrays;
12 |
13 | public class PushServiceProvider {
14 |
15 | private static PushServiceProvider instance;
16 |
17 | private PushService pushService;
18 | private Exception registrationException;
19 |
20 | protected PushServiceProvider() {
21 | pushService = new PushService.Builder().openshift().build();
22 | }
23 |
24 | /**
25 | * Register the push service.
26 | */
27 | public void registerDevice() {
28 | pushService.registerDevice().respondOn(new AppExecutors().singleThreadService())
29 | .respondWith(new Responder() {
30 | @Override
31 | public void onResult(Boolean value) {
32 | MobileCore.getLogger().info(String.valueOf(R.string.push_device_register_success));
33 | }
34 |
35 | @Override
36 | public void onException(Exception exception) {
37 | MobileCore.getLogger().error(exception.getMessage(), exception);
38 | registrationException = exception;
39 | }
40 | });
41 | }
42 |
43 | /**
44 | * Retrieve the exception that occurred during registration, if any.
45 | * @return The exception that occurred during registration, or null.
46 | */
47 | public Exception getRegistrationException() {
48 | return this.registrationException;
49 | }
50 |
51 | public static PushServiceProvider getInstance() {
52 | if(instance == null) {
53 | instance = new PushServiceProvider();
54 | }
55 | return instance;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/views/BaseAppView.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.views;
2 |
3 | import android.app.Fragment;
4 | import android.content.Context;
5 | import android.support.design.widget.Snackbar;
6 |
7 | import com.aerogear.androidshowcase.R;
8 | import com.aerogear.androidshowcase.mvp.components.ProgressDialogHelper;
9 |
10 | /**
11 | * A base implementation for the AppView interface.
12 | */
13 |
14 | public abstract class BaseAppView implements AppView {
15 |
16 | private Fragment fragment;
17 | private ProgressDialogHelper progressDialogHelper;
18 |
19 | public BaseAppView(Fragment fragment) {
20 | this.fragment = fragment;
21 | this.progressDialogHelper = new ProgressDialogHelper();
22 |
23 | }
24 |
25 | @Override
26 | public void showLoading() {
27 | Context ctx = getContext();
28 | if (ctx != null) {
29 | this.progressDialogHelper.showProgress(ctx);
30 | }
31 | }
32 |
33 | @Override
34 | public void hideLoading() {
35 | this.progressDialogHelper.hideProgress();
36 | }
37 |
38 | @Override
39 | public void showMessage(String message) {
40 | Context ctx = getContext();
41 | if (ctx != null) {
42 | Snackbar snackbar = Snackbar.make(this.fragment.getActivity().getCurrentFocus(), message, Snackbar.LENGTH_SHORT)
43 | .setAction("Action", null);
44 | snackbar.getView().setBackgroundResource(R.color.white);
45 | snackbar.show();
46 | }
47 | }
48 |
49 | @Override
50 | public void showMessage(int messageResId) {
51 | Context ctx = getContext();
52 | showMessage(ctx.getString(messageResId));
53 | }
54 |
55 | @Override
56 | public void showMessage(int messageResourceId, Object... formatArgs) {
57 | Context ctx = getContext();
58 | showMessage(ctx.getString(messageResourceId, formatArgs));
59 | }
60 |
61 | private Context getContext() {
62 | return this.fragment.getActivity();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/domain/store/NoteStoreTestBase.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.store;
2 |
3 | import com.aerogear.androidshowcase.domain.models.Note;
4 |
5 | import java.util.List;
6 |
7 | import static junit.framework.Assert.assertEquals;
8 | import static junit.framework.Assert.assertNotNull;
9 |
10 | /**
11 | * Created by weili on 25/09/2017.
12 | */
13 |
14 | public class NoteStoreTestBase {
15 |
16 | protected void noteCRUDL(NoteDataStore noteStore) throws Exception {
17 | String noteTitle = "testNoteTitle";
18 | String noteContent = "this is a test note";
19 | //make sure no existing notes
20 | Note testNote = new Note(noteTitle, noteContent);
21 | List notes = noteStore.listNotes();
22 | assertEquals(0, notes.size());
23 |
24 | //create a note
25 | Note created = noteStore.createNote(testNote);
26 | assertNotNull(created);
27 | notes = noteStore.listNotes();
28 | assertEquals(1, notes.size());
29 | Note firstNoteInList = notes.get(0);
30 | assertNotNull(firstNoteInList.getId());
31 | assertNotNull(firstNoteInList.getTitle());
32 | assertNotNull(firstNoteInList.getCreatedAt());
33 |
34 | //read a note
35 | Note readNote = noteStore.readNote(testNote.getId());
36 | assertNotNull(readNote);
37 | assertEquals(readNote.getTitle(), testNote.getTitle());
38 | assertEquals(readNote.getContent(), testNote.getContent());
39 | assertEquals(readNote.getId(), testNote.getId());
40 |
41 | //update a note
42 | String titleUpdate = "testNoteTitleUpdated";
43 | testNote.setTitle(titleUpdate);
44 | Note updatedNote = noteStore.updateNote(testNote);
45 | assertNotNull(updatedNote);
46 | readNote = noteStore.readNote(testNote.getId());
47 | assertEquals(readNote.getTitle(), titleUpdate);
48 |
49 | //delete a note
50 | noteStore.deleteNote(testNote);
51 | notes = noteStore.listNotes();
52 | assertEquals(0, notes.size());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/device/views/WarningDialog.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.device.views;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.app.DialogFragment;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import android.support.annotation.Nullable;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 |
13 | import com.aerogear.androidshowcase.R;
14 |
15 | public class WarningDialog extends DialogFragment {
16 | int SCORE_THRESHOLD;
17 | int trustScore;
18 |
19 | @Nullable
20 | @Override
21 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
22 | View view = super.onCreateView(inflater, container, savedInstanceState);
23 | getDialog().setCanceledOnTouchOutside(false);
24 | return view;
25 | }
26 |
27 | @Override
28 | public Dialog onCreateDialog(Bundle savedInstanceState) {
29 | // Use the Builder class for convenient dialog construction
30 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
31 | SCORE_THRESHOLD = getArguments().getInt("SCORE_THRESHOLD");
32 | trustScore = getArguments().getInt("trustScore");
33 | builder.setTitle("Warning");
34 | builder.setMessage("Your current device trust score " + trustScore + "% is below the specified target of " + SCORE_THRESHOLD + "%, do you want to continue or exit the app?")
35 | .setPositiveButton(R.string.device_exit, new DialogInterface.OnClickListener() {
36 | public void onClick(DialogInterface dialog, int id) {
37 | android.os.Process.killProcess(android.os.Process.myPid());
38 | }
39 | })
40 | .setNegativeButton(R.string.device_continue, new DialogInterface.OnClickListener() {
41 | public void onClick(DialogInterface dialog, int id) {
42 | dismiss();
43 | }
44 | });
45 | return builder.create();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
25 |
26 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/underconstruction/UnderConstructionFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.underconstruction;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import com.aerogear.androidshowcase.R;
11 | import com.aerogear.androidshowcase.features.push.presenters.PushPresenter;
12 | import com.aerogear.androidshowcase.features.underconstruction.presenters.UnderConstructionPresenter;
13 | import com.aerogear.androidshowcase.features.underconstruction.views.UnderConstructionViewImpl;
14 | import com.aerogear.androidshowcase.mvp.presenters.Presenter;
15 | import com.aerogear.androidshowcase.mvp.views.AppView;
16 | import com.aerogear.androidshowcase.mvp.views.BaseFragment;
17 |
18 | import javax.inject.Inject;
19 |
20 | import butterknife.ButterKnife;
21 | import dagger.android.AndroidInjection;
22 |
23 | public class UnderConstructionFragment extends BaseFragment {
24 |
25 | public static final String TAG = "UnderConstruction";
26 |
27 | @Inject
28 | UnderConstructionPresenter underConstructionPresenter;
29 |
30 | @Nullable
31 | @Override
32 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
33 | Bundle savedInstanceState) {
34 | View view = inflater.inflate(R.layout.fragment_under_construction, container, false);
35 |
36 | ButterKnife.bind(this, view);
37 |
38 | return view;
39 | }
40 |
41 | @Override
42 | public void onAttach(Activity activity) {
43 | AndroidInjection.inject(this);
44 | super.onAttach(activity);
45 | }
46 |
47 | @Override
48 | public void onDetach() {
49 | super.onDetach();
50 | this.underConstructionPresenter = null;
51 | }
52 |
53 | @Override
54 | protected Presenter initPresenter() {
55 | return underConstructionPresenter;
56 | }
57 |
58 | @Override
59 | protected AppView initView() {
60 | return new UnderConstructionViewImpl(this) {
61 | };
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/CertificateErrorDialog.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.app.DialogFragment;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import android.support.annotation.Nullable;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 |
13 | public class CertificateErrorDialog extends DialogFragment {
14 |
15 |
16 | private Runnable gotoDocs;
17 |
18 | @Nullable
19 | @Override
20 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
21 | View view = super.onCreateView(inflater, container, savedInstanceState);
22 | getDialog().setCanceledOnTouchOutside(false);
23 | return view;
24 | }
25 |
26 | @Override
27 | public Dialog onCreateDialog(Bundle savedInstanceState) {
28 | // Use the Builder class for convenient dialog construction
29 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
30 | builder.setTitle("Certificate Error");
31 | builder.setMessage("You may be using self signed certificates that will prevent the showcase from running properly. Please review the documentation and configure your certificates.")
32 | .setPositiveButton("Show Documentation", new DialogInterface.OnClickListener() {
33 | public void onClick(DialogInterface dialog, int id) {
34 | if (gotoDocs != null) {
35 | gotoDocs.run();
36 | } else {
37 | dismiss();
38 | }
39 | }
40 | })
41 | .setNegativeButton("Close", new DialogInterface.OnClickListener() {
42 | public void onClick(DialogInterface dialog, int id) {
43 | dismiss();
44 | }
45 | });
46 | return builder.create();
47 | }
48 |
49 | public void setGotoDocs(Runnable runnable) {
50 | this.gotoDocs = runnable;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/di/SecureApplicationTestComponent.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import android.content.Context;
4 |
5 | import com.aerogear.androidshowcase.AesCryptoTest;
6 | import com.aerogear.androidshowcase.RsaCryptoTest;
7 | import com.aerogear.androidshowcase.StorageFeatureTest;
8 | import com.aerogear.androidshowcase.domain.repositories.NoteRepository;
9 | import com.aerogear.androidshowcase.domain.store.NoteDataStoreFactory;
10 | import com.aerogear.androidshowcase.domain.store.SecureFileNoteStoreTest;
11 | import com.aerogear.androidshowcase.domain.store.sqlite.SqliteNoteStoreTest;
12 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
13 |
14 | import javax.inject.Singleton;
15 |
16 | import dagger.BindsInstance;
17 | import dagger.Component;
18 | import dagger.android.AndroidInjectionModule;
19 |
20 | /**
21 | * Define the DI graph for Dagger for the tests. You will need to add new methods into this interface if new tests are added and then need DI.
22 | */
23 | @Singleton
24 | @Component(modules = {AndroidInjectionModule.class, SecureApplicationTestModule.class, MainActivityModule.class})
25 | public interface SecureApplicationTestComponent {
26 | @Component.Builder
27 | interface Builder {
28 | @BindsInstance
29 | SecureApplicationTestComponent.Builder application(SecureTestApplication application);
30 |
31 | SecureApplicationTestComponent build();
32 | }
33 |
34 | void inject(SecureTestApplication app);
35 |
36 | //when a new test is added, if it needs DI, please add a new "inject" method here. The argument should be an instance of the test.
37 | //Then in the test itself, make sure call this method during setup. See StorageFeatureTest for example.
38 | void inject(StorageFeatureTest fragmentTest);
39 | void inject(AesCryptoTest cryptoTest);
40 | void inject(RsaCryptoTest rsaTest);
41 | void inject(SqliteNoteStoreTest noteStoreTest);
42 | void inject(SecureFileNoteStoreTest fileNoteStoreTest);
43 |
44 | Context context();
45 | NoteDataStoreFactory provideNoteDataStoreFactory();
46 | NoteRepository noteRepository();
47 | OpenIDAuthenticationProvider authProvider();
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/AesCryptoTest.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase;
2 |
3 | import android.support.test.InstrumentationRegistry;
4 |
5 | import com.aerogear.androidshowcase.di.SecureTestApplication;
6 | import com.aerogear.androidshowcase.domain.crypto.AesCrypto;
7 | import com.aerogear.androidshowcase.domain.utils.StreamUtils;
8 |
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | import java.io.ByteArrayInputStream;
13 | import java.io.ByteArrayOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.OutputStream;
17 | import java.security.GeneralSecurityException;
18 |
19 | import javax.inject.Inject;
20 |
21 | import static junit.framework.Assert.assertEquals;
22 |
23 | /**
24 | * Created by weili on 22/09/2017.
25 | */
26 |
27 | public class AesCryptoTest {
28 |
29 | @Inject
30 | AesCrypto cryptoToTest;
31 |
32 | @Before
33 | public void setUp() {
34 | SecureTestApplication application = (SecureTestApplication) InstrumentationRegistry.getTargetContext().getApplicationContext();
35 | application.getComponent().inject(this);
36 | }
37 |
38 | @Test
39 | public void testCryptoOperations() throws GeneralSecurityException, IOException {
40 | String testKeyAlias = "AesGcmCryptoTestKey";
41 | String textToEncrypt = "this is a test text";
42 | String encryptedText = cryptoToTest.encryptString(testKeyAlias, textToEncrypt, "utf-8");
43 | String decryptedText = cryptoToTest.decryptString(testKeyAlias, encryptedText, "utf-8");
44 | assertEquals(textToEncrypt, decryptedText);
45 |
46 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
47 | OutputStream cipherStream = cryptoToTest.encryptStream(testKeyAlias, bos);
48 | cipherStream.write(textToEncrypt.getBytes());
49 | cipherStream.flush();
50 | cipherStream.close();
51 | byte[] encryptedBytes = bos.toByteArray();
52 | ByteArrayInputStream bis = new ByteArrayInputStream(encryptedBytes);
53 | InputStream decryptedStream = cryptoToTest.decryptStream(testKeyAlias, bis);
54 | String decryptedTextFromStream = StreamUtils.readStream(decryptedStream);
55 | assertEquals(textToEncrypt, decryptedTextFromStream);
56 | }
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
25 |
26 |
37 |
38 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/NullAndroidSecureKeyStore.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import java.io.IOException;
4 | import java.security.GeneralSecurityException;
5 | import java.security.Key;
6 | import java.security.KeyStore;
7 |
8 | /**
9 | * A implementation of the SecureKeyStore interface that will only throw exceptions.
10 | * This should only be used for devices with Android that is lower than 18 (4.3).
11 | */
12 |
13 | public class NullAndroidSecureKeyStore extends SecureKeyStoreImpl implements SecureKeyStore {
14 | @Override
15 | public String getSupportedAESMode() {
16 | return null;
17 | }
18 |
19 | @Override
20 | public String getSupportedRSAMode() {
21 | return null;
22 | }
23 |
24 | @Override
25 | public boolean hasSecretKey(String keyAlias) throws GeneralSecurityException, IOException {
26 | throw new GeneralSecurityException("This version of Android is not supported");
27 | }
28 |
29 | @Override
30 | public Key getSecretKey(String keyAlias) throws GeneralSecurityException, IOException {
31 | throw new GeneralSecurityException("This version of Android is not supported");
32 | }
33 |
34 | @Override
35 | public void generateAESKey(String keyAlias) throws GeneralSecurityException, IOException {
36 | throw new GeneralSecurityException("This version of Android is not supported");
37 | }
38 |
39 | @Override
40 | public void generatePrivateKeyPair(String keyAlias) throws GeneralSecurityException, IOException {
41 | throw new GeneralSecurityException("This version of Android is not supported");
42 | }
43 |
44 | @Override
45 | public KeyStore.Entry getKeyPairEntry(String keyAlias) throws GeneralSecurityException, IOException {
46 | throw new GeneralSecurityException("This version of Android is not supported");
47 | }
48 |
49 | @Override
50 | public boolean hasKeyPair(String keyAlias) throws GeneralSecurityException, IOException {
51 | throw new GeneralSecurityException("This version of Android is not supported");
52 | }
53 |
54 | @Override
55 | public void deleteKey(String keyAlias) throws GeneralSecurityException, IOException {
56 | throw new GeneralSecurityException("This version of Android is not supported");
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | /**
2 | * Android Jenkinsfile
3 | */
4 | node("android"){
5 | stage("Checkout"){
6 | checkout scm
7 | }
8 |
9 | stage ("Prepare"){
10 | sh 'chmod +x ./gradlew'
11 | }
12 |
13 | stage("Build"){
14 | if (params.BUILD_CONFIG == 'release') {
15 | sh './gradlew clean assembleRelease' // builds app/build/outputs/apk/app-release.apk file
16 | } else {
17 | sh './gradlew clean assembleDebug' // builds app/build/outputs/apk/app-debug.apk
18 | }
19 | }
20 |
21 | def keyStoreId = params.BUILD_CREDENTIAL_ID
22 | def keyAlias = params.BUILD_CREDENTIAL_ALIAS ?: ''
23 | // Uncomment this stage if your keystore is external to your source code.
24 | // stage("Sign"){
25 | // if (params.BUILD_CONFIG == 'release') {
26 | // signAndroidApks (
27 | // keyStoreId: keyStoreId,
28 | // keyAlias: keyAlias,
29 | // apksToSign: "**/*-unsigned.apk",
30 | // uncomment the following line to output the signed APK to a separate directory as described above
31 | // signedApkMapping: [ $class: UnsignedApkBuilderDirMapping ],
32 | // uncomment the following line to output the signed APK as a sibling of the unsigned APK, as described above, or just omit signedApkMapping
33 | // you can override these within the script if necessary
34 | // androidHome: '/usr/local/Cellar/android-sdk'
35 | // )
36 | // } else {
37 | // println('Debug Build - Using default developer signing key')
38 | // }
39 | // }
40 |
41 | stage('Kryptowire') {
42 | //using a try-catch block so the pipeline script won't fail if the krypowire plugin is not installed
43 | try {
44 | if (params.BUILD_CONFIG == 'release') {
45 | kwSubmit filePath: "app/build/outputs/apk/release/app-release.apk", platform: 'android'
46 | } else {
47 | kwSubmit filePath: "app/build/outputs/apk/debug/app-debug.apk", platform: 'android'
48 | }
49 | } catch(Error e) {
50 | e.printStackTrace()
51 | }
52 | }
53 |
54 |
55 |
56 | stage("Archive"){
57 | if (params.BUILD_CONFIG == 'release') {
58 | archiveArtifacts artifacts: 'app/build/outputs/apk/**/app-release.apk', excludes: 'app/build/outputs/apk/*-unaligned.apk'
59 | } else {
60 | archiveArtifacts artifacts: 'app/build/outputs/apk/**/app-debug.apk', excludes: 'app/build/outputs/apk/*-unaligned.apk'
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_push.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
15 |
19 |
20 |
31 |
32 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_authentication.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
17 |
18 |
31 |
32 |
41 |
42 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/navigation/PushRegistrationFailedDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.navigation;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.support.v4.app.DialogFragment;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import android.view.LayoutInflater;
9 |
10 | import com.aerogear.androidshowcase.R;
11 |
12 | public class PushRegistrationFailedDialogFragment extends DialogFragment {
13 |
14 | private static final String REGISTRATION_ERROR_KEY = "serviceType";
15 |
16 | private String registrationErrorMessage;
17 |
18 | public PushRegistrationFailedDialogFragment() {
19 | }
20 |
21 | /**
22 | * Use this factory method to create a new instance of
23 | * this fragment using the provided parameters.
24 | *
25 | * @param registrationException The exception thrown during registration.
26 | * @return A new instance of fragment NotAvailableDialogFragment.
27 | */
28 | public static PushRegistrationFailedDialogFragment newInstance(Exception registrationException) {
29 | PushRegistrationFailedDialogFragment fragment = new PushRegistrationFailedDialogFragment();
30 | Bundle args = new Bundle();
31 | args.putString(REGISTRATION_ERROR_KEY, registrationException.getMessage());
32 | fragment.setArguments(args);
33 | return fragment;
34 | }
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | if (getArguments() != null) {
40 | this.registrationErrorMessage = getArguments().getString(REGISTRATION_ERROR_KEY);
41 | }
42 | }
43 |
44 | @Override
45 | public Dialog onCreateDialog(Bundle savedInstanceState) {
46 | // Use the Builder class for convenient dialog construction
47 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
48 | LayoutInflater inflater = getActivity().getLayoutInflater();
49 | builder.setCustomTitle(inflater.inflate(R.layout.push_registration_failed_title, null));
50 | builder.setMessage(String.format("The push service failed to register.\n\nDetails: %s", registrationErrorMessage))
51 | .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
52 | public void onClick(DialogInterface dialog, int id) {
53 | dismiss();
54 | }
55 | });
56 | return builder.create();
57 | }
58 |
59 | @Override
60 | public void onDetach() {
61 | super.onDetach();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_landing_security.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
26 |
27 |
42 |
43 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/RsaHelper.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import com.aerogear.androidshowcase.domain.utils.StreamUtils;
4 |
5 | import java.io.ByteArrayInputStream;
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.security.GeneralSecurityException;
9 | import java.security.KeyStore;
10 |
11 | import javax.crypto.Cipher;
12 | import javax.crypto.CipherInputStream;
13 | import javax.crypto.CipherOutputStream;
14 |
15 | /**
16 | * Created by weili on 27/09/2017.
17 | */
18 |
19 | public class RsaHelper {
20 |
21 |
22 | // tag::encrypt[]
23 | /**
24 | * Perform encryption using RSA
25 | * @param mode the RSA encryption alg
26 | * @param keyEntry the private/public key entry
27 | * @param text the data to encrypt
28 | * @return the encrypted ddata
29 | * @throws GeneralSecurityException
30 | * @throws IOException
31 | */
32 | public static byte[] encrypt(String mode, KeyStore.PrivateKeyEntry keyEntry, byte[] text) throws GeneralSecurityException, IOException {
33 | // Encrypt the text
34 | Cipher inputCipher = Cipher.getInstance(mode);
35 | inputCipher.init(Cipher.ENCRYPT_MODE, keyEntry.getCertificate().getPublicKey());
36 | //The key to encrypt should be either 16 (128 bit) or 32 (256 bit) in size, well below the block size for RSA (should be around 214 bytes)
37 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
38 | CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inputCipher);
39 | cipherOutputStream.write(text);
40 | cipherOutputStream.close();
41 |
42 | byte[] vals = outputStream.toByteArray();
43 | return vals;
44 | }
45 | // end::encrypt[]
46 |
47 | // tag::decrypt[]
48 | /**
49 | * Perform decryption using RSA
50 | * @param mode the RSA decryption alg
51 | * @param keyEntry the private/public key entry
52 | * @param toDecrypt the encrypted data
53 | * @return the decrypted data
54 | * @throws GeneralSecurityException
55 | * @throws IOException
56 | */
57 | public static byte[] decrypt(String mode, KeyStore.PrivateKeyEntry keyEntry, byte[] toDecrypt) throws GeneralSecurityException, IOException {
58 | Cipher output = Cipher.getInstance(mode);
59 | output.init(Cipher.DECRYPT_MODE, keyEntry.getPrivateKey());
60 | ByteArrayInputStream inputStream = new ByteArrayInputStream(toDecrypt);
61 | CipherInputStream cipherInputStream = new CipherInputStream(inputStream, output);
62 | return StreamUtils.readStreamBytes(cipherInputStream);
63 | }
64 | // end::decrypt[]
65 | }
66 |
--------------------------------------------------------------------------------
/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/layout/fragment_under_construction.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
25 |
26 |
41 |
42 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_sidebar_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
80 |
--------------------------------------------------------------------------------
/readme.adoc:
--------------------------------------------------------------------------------
1 | image:https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat["API", link="https://android-arsenal.com/api?level=21"]
2 | image:https://snyk.io/test/github/aerogear/android-showcase-template/master%2Fapp/badge.svg?style=svg["Known Vulnerabilities", link="https://snyk.io/test/github/aerogear/android-showcase-template/master%2Fapp"]
3 |
4 | = Secure Native Android Template
5 |
6 | A Native Android Application that demonstrates Secure Mobile Development Practises.
7 |
8 | For more information, please check the https://github.com/feedhenry/mobile-security[Mobile Security Project].
9 |
10 | == Binary Scanning with Kryptowire
11 |
12 | Everytime when a PR is merged to master, the build script will automatically push the built binary to Kryptowire for security scanning.
13 |
14 | == Features
15 | - OpenID Connect Authentication
16 | - Mobile Access Control
17 | - Client Cert Authentication
18 | - Certificate Pinning
19 | - Code Obfuscation (with Proguard)
20 | - Secure Data Storage
21 | - Device Trust Checks (Root Access, Emulator Access etc)
22 | - 2FA with OTP (via Keycloak)
23 | - Authentication Brute Force Detection (via Keycloak)
24 | - Account Lockout Policies (via Keycloak)
25 | - Authentication/Access Control Auditing & Logging (via Keycloak)
26 | - Authenticated Calls to Protected Endpoints (via Keycloak)
27 |
28 | == Run Showcase
29 | The `master` branch will always track to the latest release of the SDK.
30 |
31 | To run the showcase app
32 |
33 | [source, bash]
34 | ----
35 | git clone git@github.com:aerogear/android-showcase-template.git
36 | ----
37 |
38 | Open showcase app in Android Studio and select the build variant `release` or `debug` to run the showcase with the latest release, or select build variant `local` to run the show case with a locally installed version of the https://github.com/aerogear/aerogear-android-sdk/blob/master/docs/modules/ROOT/pages/index.adoc[SDK]
39 |
40 | == Build Instructions
41 |
42 | For full build instructions please take some time to read our link:./CONTRIBUTING.md[Contributing Guide]
43 |
44 | == Work with Different Backend Services
45 |
46 | By default the app will use the backend services that are running on https://security.skunkhenry.com:8443[a dedicated OpenShift cluster] for demonstration purpose.
47 |
48 | You can also configure the app to run against different backend services:
49 |
50 | === Keycloak Configuration
51 | The Keycloak configuration is saved in the https://github.com/aerogear/android-showcase-template/tree/master/app/src/main/assets/mobile-services.json[mobile-services.json] file.
52 |
53 | .Code
54 | [source,json,linenums,indent=0]
55 | ....
56 | include::https://raw.githubusercontent.com/aerogear/android-showcase-template/master/app/src/main/assets/mobile-services.json[]
57 | ....
58 |
59 | == Work with Self-signed Certificate
60 |
61 | By default, the app will not work with self-signed certificate due to security reasons. However, to help with local development, you may need to support it. link:https://docs.aerogear.org/aerogear/latest/getting-started.html#using-self-signed-certificates-in-mobile-apps[Docs.aerogear.org] has full configuration instructions.
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/SecureKeyStore.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import java.io.IOException;
4 | import java.security.GeneralSecurityException;
5 | import java.security.Key;
6 | import java.security.KeyStore;
7 |
8 | /**
9 | * A keystore interface. It will allow us to support different types of keystores on different versions of Android.
10 | */
11 |
12 | public interface SecureKeyStore {
13 |
14 | /**
15 | * Return the supported AES mode.
16 | * @return the supported AES mode, like AES/GCM/NoPadding
17 | */
18 | public String getSupportedAESMode();
19 |
20 | /**
21 | * Return the supported RSA mode
22 | * @return the supported RSA mode, like RSA/ECB/PKCS1Padding
23 | */
24 | public String getSupportedRSAMode();
25 |
26 | /**
27 | * Check if the keystore has the given secret key alias
28 | * @param keyAlias the key alias of the secret key to check
29 | * @return if the key alias exists
30 | * @throws GeneralSecurityException
31 | * @throws IOException
32 | */
33 | public boolean hasSecretKey(String keyAlias) throws GeneralSecurityException, IOException;
34 |
35 | /**
36 | * Get the secret key from the key store
37 | * @param keyAlias the alias of the secret key
38 | * @return the secret key
39 | * @throws GeneralSecurityException
40 | * @throws IOException
41 | */
42 | public Key getSecretKey(String keyAlias) throws GeneralSecurityException, IOException;
43 |
44 | /**
45 | * Generate a AES secret key and save in the key store
46 | * @param keyAlias the alias of the secret key
47 | * @throws GeneralSecurityException
48 | * @throws IOException
49 | */
50 | public void generateAESKey(String keyAlias) throws GeneralSecurityException, IOException;
51 |
52 | /**
53 | * Generate a private key pair in the key store
54 | * @param keyAlias the alias of the key pair
55 | * @throws GeneralSecurityException
56 | * @throws IOException
57 | */
58 | public void generatePrivateKeyPair(String keyAlias) throws GeneralSecurityException, IOException;
59 |
60 | /**
61 | * Get the key pair from the key store
62 | * @param keyAlias the alias of the key pair
63 | * @return the key pair
64 | * @throws GeneralSecurityException
65 | * @throws IOException
66 | */
67 | public KeyStore.Entry getKeyPairEntry(String keyAlias) throws GeneralSecurityException, IOException;
68 |
69 | /**
70 | * Check if the key store has the given ken alias
71 | * @param keyAlias the alias of the key
72 | * @return
73 | * @throws GeneralSecurityException
74 | * @throws IOException
75 | */
76 | public boolean hasKeyPair(String keyAlias) throws GeneralSecurityException, IOException;
77 |
78 | /**
79 | * Delete the key from the key store with the given key alias
80 | * @param keyAlias
81 | * @throws GeneralSecurityException
82 | * @throws IOException
83 | */
84 | public void deleteKey(String keyAlias) throws GeneralSecurityException, IOException;
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/documentation/DocumentationFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.documentation;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.webkit.WebResourceRequest;
10 | import android.webkit.WebView;
11 | import android.webkit.WebViewClient;
12 |
13 | import com.aerogear.androidshowcase.features.documentation.presenters.DocumentationPresenter;
14 | import com.aerogear.androidshowcase.features.documentation.views.DocumentationView;
15 | import com.aerogear.androidshowcase.features.documentation.views.DocumentationViewImpl;
16 | import com.aerogear.androidshowcase.mvp.views.BaseFragment;
17 |
18 | import java.net.URL;
19 |
20 | import javax.inject.Inject;
21 |
22 | import dagger.android.AndroidInjection;
23 |
24 | public class DocumentationFragment extends BaseFragment {
25 |
26 | public static final String TAG = "home";
27 | private static final String DOC_URL = "DocumentationFragment.DOC_URL";
28 |
29 | @Inject
30 | DocumentationPresenter documentationPresenter;
31 |
32 | private String url;
33 |
34 | public DocumentationFragment() {
35 | }
36 |
37 |
38 | @Override
39 | public void onAttach(Activity activity) {
40 | AndroidInjection.inject(this);
41 | super.onAttach(activity);
42 | }
43 |
44 | @Override
45 | public void onDetach() {
46 | super.onDetach();
47 | this.documentationPresenter = null;
48 | }
49 |
50 | @Override
51 | protected DocumentationPresenter initPresenter() {
52 | return documentationPresenter;
53 | }
54 |
55 | @Override
56 | protected DocumentationView initView() {
57 | return new DocumentationViewImpl(this);
58 | }
59 |
60 | public static DocumentationFragment newInstance(DocumentUrl url) {
61 | DocumentationFragment fragment = new DocumentationFragment();
62 |
63 | Bundle args = new Bundle();
64 | args.putString(DOC_URL, url.getUrl());
65 | fragment.setArguments(args);
66 | return fragment;
67 | }
68 |
69 | @Override
70 | public void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | if (getArguments() != null) {
73 | this.url = getArguments().getString(DOC_URL);
74 | }
75 | }
76 |
77 | @Nullable
78 | @Override
79 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
80 | WebView webview = new WebView(this.getActivity());
81 |
82 | webview.setWebViewClient(new WebViewClient() {
83 | @Override
84 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
85 | view.loadUrl(request.getUrl().toString());
86 | return false;
87 | }
88 | });
89 |
90 | webview.getSettings().setJavaScriptEnabled(true);
91 | webview.loadUrl(this.url);
92 |
93 | return webview;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/components/HttpHelper.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.components;
2 |
3 | import android.content.Context;
4 |
5 | import org.aerogear.mobile.core.MobileCore;
6 | import org.aerogear.mobile.core.configuration.MobileCoreConfiguration;
7 | import org.aerogear.mobile.core.configuration.MobileCoreJsonParser;
8 | import org.aerogear.mobile.core.configuration.ServiceConfiguration;
9 | import org.aerogear.mobile.core.executor.AppExecutors;
10 | import org.aerogear.mobile.core.http.HttpServiceModule;
11 | import org.aerogear.mobile.core.reactive.Request;
12 | import org.aerogear.mobile.core.reactive.Requester;
13 |
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.util.Map;
17 | import java.util.concurrent.atomic.AtomicBoolean;
18 |
19 | import okhttp3.OkHttpClient;
20 | import okhttp3.Response;
21 |
22 | /**
23 | * Created by tjackman on 11/10/17.
24 | */
25 |
26 | public class HttpHelper {
27 |
28 | private static OkHttpClient.Builder httpClient;
29 | private static Boolean CERTIFICATES_CONFIGURED = null;//Start unset
30 | public HttpHelper() {
31 |
32 | }
33 |
34 | public static void init() {
35 | httpClient = new OkHttpClient.Builder();
36 | }
37 |
38 | public static Request checkCertificates(Context context) {
39 | return Requester.call(() -> {
40 | if (CERTIFICATES_CONFIGURED == null) {
41 | InputStream jsonStream = context.getAssets().open("mobile-services.json");
42 |
43 | if (jsonStream != null) {
44 | MobileCoreConfiguration configuration = new MobileCoreJsonParser(jsonStream).parse();
45 | Map configurations = configuration.getServicesConfigPerId();
46 |
47 | if (configurations.size() != 0) {
48 | HttpServiceModule httpService = MobileCore.getInstance().getHttpLayer();
49 | ServiceConfiguration serviceConfiguration = configurations.values().iterator().next();
50 | String serviceUrl = serviceConfiguration.getUrl();
51 | OkHttpClient client = new OkHttpClient();
52 | okhttp3.Request okHttpRequest = new okhttp3.Request.Builder().url(serviceUrl).get().build();
53 | try {
54 | Response response = client.newCall(okHttpRequest).execute();
55 | response.body().close();
56 | CERTIFICATES_CONFIGURED = true; //Call didn't blow up
57 | } catch (Exception ex) {
58 | CERTIFICATES_CONFIGURED = false;
59 | }
60 |
61 | } else {
62 | CERTIFICATES_CONFIGURED = true; //No Services to call, so all is OK,
63 | }
64 |
65 | } else {
66 | CERTIFICATES_CONFIGURED = false;
67 | }
68 |
69 |
70 |
71 | }
72 | return CERTIFICATES_CONFIGURED;
73 | }).requestOn(new AppExecutors().networkThread());
74 |
75 | }
76 |
77 | public static OkHttpClient.Builder getHttpClient() {
78 | return httpClient;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/navigation/NotAvailableDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.navigation;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.DialogInterface;
6 | import android.os.Bundle;
7 | import android.support.v4.app.DialogFragment;
8 | import android.view.LayoutInflater;
9 |
10 | import com.aerogear.androidshowcase.R;
11 |
12 | /**
13 | * This fragment displays the "Feature not available" dialog.
14 | */
15 | public class NotAvailableDialogFragment extends DialogFragment {
16 |
17 | private static final String SERVICE_TYPE_KEY = "serviceType";
18 |
19 |
20 | private String serviceType;
21 | private GotoDocsListener gotoDocsCallback;
22 |
23 | public NotAvailableDialogFragment() {
24 | }
25 |
26 | /**
27 | * Use this factory method to create a new instance of
28 | * this fragment using the provided parameters.
29 | *
30 | * @param serviceType serviceType name to lookup.
31 | * @return A new instance of fragment NotAvailableDialogFragment.
32 | */
33 | public static NotAvailableDialogFragment newInstance(String serviceType) {
34 | NotAvailableDialogFragment fragment = new NotAvailableDialogFragment();
35 | Bundle args = new Bundle();
36 | args.putString(SERVICE_TYPE_KEY, serviceType);
37 | fragment.setArguments(args);
38 | return fragment;
39 | }
40 |
41 | @Override
42 | public void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | if (getArguments() != null) {
45 | this.serviceType = getArguments().getString(SERVICE_TYPE_KEY);
46 | }
47 | }
48 |
49 | @Override
50 | public Dialog onCreateDialog(Bundle savedInstanceState) {
51 | // Use the Builder class for convenient dialog construction
52 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
53 | LayoutInflater inflater = getActivity().getLayoutInflater();
54 | builder.setCustomTitle(inflater.inflate(R.layout.fragment_not_available_title, null));
55 | builder.setMessage(String.format("The service %s does not have a configuration in mobile-services.json. Refer to the documentation for instructions on how to configure this service.", serviceType))
56 | .setPositiveButton(R.string.show_documentation, new DialogInterface.OnClickListener() {
57 | public void onClick(DialogInterface dialog, int id) {
58 | if (gotoDocsCallback != null) {
59 | gotoDocsCallback.goToDocs();
60 | } else {
61 | dismiss();
62 | }
63 | }
64 | })
65 | .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
66 | public void onClick(DialogInterface dialog, int id) {
67 | dismiss();
68 | }
69 | });
70 | return builder.create();
71 | }
72 |
73 | @Override
74 | public void onDetach() {
75 | super.onDetach();
76 | }
77 |
78 | public void setGotoDocsCallback(GotoDocsListener gotoDocsCallback) {
79 | this.gotoDocsCallback = gotoDocsCallback;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at aerogear-dev@lists.jboss.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_network.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
22 |
23 |
34 |
35 |
45 |
46 |
56 |
57 |
69 |
70 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_landing.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
26 |
27 |
41 |
42 |
54 |
55 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/authentication/providers/KeycloakAuthenticateProviderImpl.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.authentication.providers;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.util.Log;
8 | import com.aerogear.androidshowcase.R;
9 | import org.aerogear.mobile.auth.AuthService;
10 | import org.aerogear.mobile.auth.authenticator.DefaultAuthenticateOptions;
11 | import org.aerogear.mobile.auth.user.UserPrincipal;
12 | import org.aerogear.mobile.core.Callback;
13 |
14 | import javax.inject.Inject;
15 | import javax.inject.Singleton;
16 |
17 | /**
18 | * Created by tjackman on 9/8/17.
19 | */
20 |
21 | @Singleton
22 | public class KeycloakAuthenticateProviderImpl implements OpenIDAuthenticationProvider {
23 |
24 | public static int LOGIN_RESULT_CODE = 1;
25 | private Callback logoutCallback;
26 |
27 | @Inject
28 | Context context;
29 |
30 | @Inject @Nullable
31 | AuthService authService;
32 |
33 | @Inject
34 | public KeycloakAuthenticateProviderImpl(@NonNull final Context context) {
35 | this.context = context;
36 | }
37 |
38 | // tag::login[]
39 | /**
40 | * Create the config for the initial Keycloak auth request to get a temporary token and create an intent to handle the response
41 | *
42 | * @param fromActivity the activity used to perform the login
43 | * @param authCallback the authentication callback
44 | */
45 | @Override
46 | public void login(final Activity fromActivity, final Callback authCallback) {
47 |
48 | // Build the options object and start the authentication flow. Provide an activity to handle the auth response.
49 | DefaultAuthenticateOptions options = new DefaultAuthenticateOptions(fromActivity, LOGIN_RESULT_CODE);
50 | authService.login(options, authCallback);
51 | }
52 | // end::login[]
53 |
54 | // tag::logout[]
55 | /**
56 | * Perform a logout request against the openid connect server
57 | *
58 | * @param logoutCallback the logout callback
59 | */
60 | public void logout(final Callback logoutCallback) {
61 | this.logoutCallback = logoutCallback;
62 | UserPrincipal currentUser = authService.currentUser();
63 | authService.logout(currentUser, new Callback() {
64 | @Override
65 | public void onSuccess() {
66 | logoutSuccess();
67 | }
68 |
69 | @Override
70 | public void onError(Throwable error) {
71 | logoutFailed(error);
72 | }
73 | });
74 | }
75 | // end::logout[]
76 |
77 | /**
78 | * Handler for a successful logout
79 | */
80 | private void logoutSuccess() {
81 | Log.w("", context.getString(R.string.logout_success));
82 | if (this.logoutCallback != null) {
83 | logoutCallback.onSuccess(null);
84 | }
85 | }
86 |
87 | /**
88 | * Handler for a failed logout
89 | *
90 | * @param error the logout error exception
91 | */
92 | private void logoutFailed(final Throwable error) {
93 | Log.w("", context.getString(R.string.logout_failed), error);
94 | if (this.logoutCallback != null) {
95 | logoutCallback.onError(error);
96 | }
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at #aerogear channel on freenode IRC or via aerogear@googlegroups.com, see [AeroGear forum](https://groups.google.com/forum/#!forum/aerogear). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 | For more information about the AeroGear community and project , visit [our website](https://aerogear.org/community/).
39 |
40 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
41 |
42 | ## Attribution
43 |
44 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
45 |
46 | [homepage]: http://contributor-covenant.org
47 | [version]: http://contributor-covenant.org/version/1/4/
48 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | Thank you for your interest in contributing to the AeroGear project. We want
4 | keep this process as easy as possible so we've outlined a few guidelines below.
5 | For more information about the AeroGear community and project , visit
6 | [our website](https://aerogear.org/community/).
7 |
8 | ## Asking for help
9 |
10 | Whether you're contributing a new feature or bug fix, or simply submitting a ticket, the AeroGear team is available for technical advice or feedback.
11 | You can reach us at #aerogear on [Freenode IRC](https://freenode.net/) or join the [mailing list](https://groups.google.com/forum/#!forum/aerogear)
12 | -- both are actively monitored.
13 |
14 | ## Getting started
15 |
16 | * Make sure you have a [JIRA account](https://issues.jboss.org)
17 | * Make sure you have a [GitHub account](https://github.com/signup/free)
18 | * Submit a ticket for your issue to the
19 | [AeroGear project]("https://issues.jboss.org/projects/AEROGEAR), assuming one does
20 | not already exist.
21 | * Clearly describe the issue including steps to reproduce when it is a bug.
22 | * Make sure you fill in the earliest version that you know has the issue.
23 | * Fork the repository on GitHub.
24 |
25 | ## Making changes
26 |
27 | * Create a topic branch from where you want to base your work.
28 | * This is usually the master branch.
29 | * To quickly create a topic branch based on master; `git checkout -b
30 | master`. By convention we typically include the JIRA issue
31 | key in the branch name, e.g. `AEROGEAR-1234-my-feature`.
32 | * Please avoid working directly on the `master` branch.
33 | * Make commits of logical units.
34 | * Prepend your commit messages with a JIRA ticket number, e.g. "AEROGEAR-1234: Fix
35 | spelling mistake in README."
36 | * Follow the coding style in use.
37 | * Check for unnecessary whitespace with `git diff --check` before committing.
38 | * Make sure you have added the necessary tests for your changes.
39 | * Run _all_ the tests to assure nothing else was accidentally broken.
40 |
41 | ## Submitting changes
42 |
43 | * Push your changes to a topic branch in your fork of the repository.
44 | * Submit a pull request to the repository in the [AeroGear GitHub organization]
45 | (https://github.com/aerogear) and choose branch you want to patch
46 | (usually master).
47 | * Advanced users may want to install the [GitHub CLI](https://hub.github.com/)
48 | and use the `hub pull-request` command.
49 | * Update your JIRA ticket to mark that you have submitted code and are ready
50 | for it to be reviewed (Status: Dev Complete).
51 | * Include a link to the pull request in the ticket.
52 | * Add detail about the change to the pull request including screenshots
53 | if the change affects the UI.
54 |
55 | ## Reviewing changes
56 |
57 | * After submitting a pull request, one of AeroGear team members will review it.
58 | * Changes may be requested to conform to our style guide and internal
59 | requirements.
60 | * When the changes are approved and all tests are passing, a AeroGear team
61 | member will merge them.
62 | * Note: if you have write access to the repository, do not directly merge pull
63 | requests. Let another team member review your pull request and approve it.
64 |
65 | # Additional Resources
66 |
67 | * [General GitHub documentation](http://help.github.com/)
68 | * [GitHub pull request documentation](https://help.github.com/articles/about-pull-requests/)
69 | * [Read the Issue Guidelines by @necolas](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md) for more details
70 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the AeroGear Android Showcase Template
2 |
3 | The AeroGear Android Showcase Template is part of the [AeroGear project](https://aerogear.org/), see the [Community Page](https://aerogear.org/community) for general guidelines for contributing to the project.
4 |
5 | This document details specifics for contributions to the Android Showcase Template.
6 |
7 | ## Issue tracker
8 |
9 | The tracking of issues for the AeroGear Android Showcase Template is done in the [AeroGear Project](https://issues.jboss.org/projects/AEROGEAR/issues) in the [JBoss Developer JIRA](https://issues.jboss.org).
10 |
11 | See the [AeroGear JIRA Usage and Guidelines Guide](https://aerogear.org/docs/guides/JIRAUsage/) for information on how the issue tracker relates to contributions to this project.
12 |
13 | ## Asking for help
14 |
15 | Whether you're contributing a new feature or bug fix, or simply submitting a
16 | ticket, the Aerogear team is available for technical advice or feedback.
17 | You can reach us at [#aerogear](ircs://chat.freenode.net:6697/aerogear) on [Freenode IRC](https://freenode.net/) or the
18 | [aerogear google group](https://groups.google.com/forum/#!forum/aerogear)
19 | -- both are actively monitored.
20 |
21 | # Developing the Android Showcase Template
22 |
23 | ## Prerequisites
24 |
25 | Ensure you have the following installed in your machine:
26 |
27 | - [Java Development Kit](https://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html)
28 | - [Android Studio and Android SDK](https://developer.android.com/studio/index.html) check [build.gradle](./app/build.gradle) file for the required SDK versions and build tools version
29 | - [Git SCM](http://git-scm.com/)
30 |
31 | ## Cloning the repository
32 |
33 | ```bash
34 | git clone git@github.com:aerogear/android-showcase-template.git
35 | cd aerogear-showcase-template/
36 | ```
37 |
38 | ## Installing dependencies and building the ShowCase Template
39 |
40 | Open showcase app in Android Studio and select the build variant `release` or `debug` to run the showcase with the latest release.
41 |
42 | With the variant selected navigate to `build` on the toolbar in Android Studio and select `Build Project`
43 |
44 | ## Installing Local Build of SDK
45 |
46 | If you are developing the Android SDK itself, it may be helpful to build the showcase using local dependencies.
47 |
48 | ### Clone and build the Android SDK
49 |
50 | ```bash
51 | git clone https://github.com/aerogear/aerogear-android-sdk
52 | cd aerogear-android-sdk
53 | ./gradlew install
54 | ```
55 |
56 | It's possible to verify the install went well by checking the local maven repository:
57 |
58 | ```bash
59 | ls ~/.m2/repository/org/aerogear
60 | # Should output android-push android-core android-auth android-security
61 | ```
62 |
63 | ### MavenLocal()
64 |
65 | If the build of the SDK is from source, add mavenLocal() to the [build.gradle](https://github.com/aerogear/android-showcase-template/blob/master/build.gradle) file in the root directory of this project.
66 |
67 | ```groovy
68 | allprojects {
69 | repositories {
70 | mavenLocal() // <-- Add This line
71 | google()
72 | jcenter()
73 | }
74 | }
75 | ```
76 |
77 | ### Reference the Dependencies
78 |
79 | In the [build.gradle](./app/build.gradle) file in the app directory, add the dependencies provided by the SDK.
80 |
81 | ```groovy
82 | dependencies {
83 | ...
84 | implementation 'org.aerogear:android-push:[version]-SNAPSHOT
85 | implementation 'org.aerogear:android-auth:[version]-SNAPSHOT
86 | implementation 'org.aerogear:android-security:[version]-SNAPSHOT
87 | }
88 | ```
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/mvp/components/CertPinningHelper.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.mvp.components;
2 |
3 | import android.support.annotation.Nullable;
4 | import com.datatheorem.android.trustkit.TrustKit;
5 | import org.aerogear.mobile.auth.AuthService;
6 | import java.io.IOException;
7 | import java.net.MalformedURLException;
8 | import java.net.URL;
9 | import java.util.concurrent.TimeUnit;
10 | import javax.inject.Inject;
11 | import javax.net.ssl.HttpsURLConnection;
12 | import javax.net.ssl.SSLSocketFactory;
13 | import javax.net.ssl.X509TrustManager;
14 | import okhttp3.Call;
15 | import okhttp3.OkHttpClient;
16 | import okhttp3.Request;
17 |
18 | /**
19 | * Created by tjackman on 9/16/17.
20 | */
21 |
22 | public class CertPinningHelper {
23 |
24 | @Inject @Nullable
25 | AuthService authService;
26 |
27 | public CertPinningHelper() {
28 |
29 | }
30 |
31 | // tag::createRequest[]
32 | /**
33 | * Make a request to a resource that requires the access token to be sent with the request
34 | *
35 | * @param requestUrl the request URL
36 | * @param sendAccessToken boolean on whether to send the access token as part of the request
37 | * @param callback the OkHTTP callback for the request
38 | */
39 | public Call createRequest(final String requestUrl, final boolean sendAccessToken, final okhttp3.Callback callback) {
40 |
41 | URL url = null;
42 | try {
43 | url = new URL(requestUrl);
44 | } catch (MalformedURLException e) {
45 | e.printStackTrace();
46 | }
47 | String serverHostname = url.getHost();
48 |
49 | HttpsURLConnection connection = null;
50 | try {
51 | connection = (HttpsURLConnection) url.openConnection();
52 | } catch (IOException e) {
53 | e.printStackTrace();
54 | }
55 |
56 | SSLSocketFactory sslSocketFactory = TrustKit.getInstance().getSSLSocketFactory(serverHostname);
57 | X509TrustManager trustManager = TrustKit.getInstance().getTrustManager(serverHostname);
58 | connection.setSSLSocketFactory(sslSocketFactory);
59 |
60 | OkHttpClient httpClient = HttpHelper.getHttpClient()
61 | .sslSocketFactory(sslSocketFactory, trustManager)
62 | .connectTimeout(10, TimeUnit.SECONDS)
63 | .build();
64 |
65 | Request request;
66 |
67 | if (sendAccessToken) {
68 |
69 | String accessToken = authService.currentUser().getAccessToken();
70 |
71 | request = new Request.Builder()
72 | .url(url)
73 | .addHeader("Authorization", String.format("Bearer %s", accessToken))
74 | .build();
75 | } else {
76 | request = new Request.Builder()
77 | .url(url)
78 | .build();
79 | }
80 |
81 |
82 | Call call = httpClient.newCall(request);
83 | call.enqueue(callback);
84 | return call;
85 | }
86 | // end::createRequest[]
87 |
88 | // tag::checkCertificateVerificationError[]
89 | /**
90 | * Check if an exception is caused by a certificate verification error
91 | *
92 | * @param error the error exception from a failed request
93 | *
94 | * @return boolean based on whether or not certificate pinning has failed
95 | */
96 | public boolean checkCertificateVerificationError(final Exception error) {
97 | boolean certificateVerificationError = false;
98 | if (error.getCause() != null &&
99 | (error.getCause().toString().contains("Certificate validation failed") ||
100 | error.getCause().toString().contains("Pin verification failed"))) {
101 | certificateVerificationError = true;
102 | }
103 | return certificateVerificationError;
104 | }
105 | // end::checkCertificateVerificationError[]
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/handler/NotificationBarMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.handler;
2 |
3 | import java.util.Date;
4 | import java.util.Map;
5 |
6 | import android.app.NotificationChannel;
7 | import android.app.NotificationManager;
8 | import android.app.PendingIntent;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.graphics.Color;
12 | import android.media.RingtoneManager;
13 | import android.os.Build;
14 | import android.support.annotation.RequiresApi;
15 | import android.support.v4.app.NotificationCompat;
16 |
17 | import com.aerogear.androidshowcase.R;
18 | import com.aerogear.androidshowcase.MainActivity;
19 | import com.aerogear.androidshowcase.SecureApplication;
20 | import com.aerogear.androidshowcase.features.push.PushFragment;
21 |
22 | import org.aerogear.mobile.push.MessageHandler;
23 | import org.aerogear.mobile.push.UnifiedPushMessage;
24 |
25 | public class NotificationBarMessageHandler implements MessageHandler {
26 |
27 | private static final String CHANNEL_ID = "AEROGEAR_PUSH_EXAMPLE";
28 | private static final String CHANNEL_NAME = "AeroGear Android Push";
29 | private static final String CHANNEL_DESCRIPTION = "AeroGear Android Push example";
30 |
31 | public static final int NOTIFICATION_ID = 1;
32 |
33 | private static final NotificationBarMessageHandler instance =
34 | new NotificationBarMessageHandler();
35 |
36 | public static NotificationBarMessageHandler getInstance() {
37 | return instance;
38 | }
39 |
40 | @Override
41 | public void onMessage(Context context, Map message) {
42 |
43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
44 | createChannel(context);
45 | }
46 |
47 | SecureApplication application = (SecureApplication) context.getApplicationContext();
48 | PushFragment.PushMessage pushMessage = new PushFragment.PushMessage(
49 | message.get(UnifiedPushMessage.MESSAGE), new Date());
50 | application.getPushMessagesReceived().add(pushMessage);
51 |
52 | displayMessageOnNotificationBar(context, message.get(UnifiedPushMessage.MESSAGE));
53 |
54 | }
55 |
56 | @RequiresApi(api = Build.VERSION_CODES.O)
57 | private void createChannel(Context context) {
58 |
59 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
60 | NotificationManager.IMPORTANCE_LOW);
61 | channel.setDescription(CHANNEL_DESCRIPTION);
62 | channel.enableLights(true);
63 | channel.setLightColor(Color.RED);
64 | channel.enableVibration(true);
65 | channel.setVibrationPattern(new long[] {100, 200, 300, 400, 500, 400, 300, 200, 400});
66 |
67 | NotificationManager notificationManager = (NotificationManager) context
68 | .getSystemService(Context.NOTIFICATION_SERVICE);
69 | notificationManager.createNotificationChannel(channel);
70 |
71 | }
72 |
73 | private void displayMessageOnNotificationBar(Context context, String message) {
74 | Intent intent = new Intent(context, MainActivity.class).putExtra(UnifiedPushMessage.MESSAGE,
75 | message);
76 | intent.putExtra(UnifiedPushMessage.class.getName(), PushFragment.class.getName());
77 |
78 | PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent,
79 | PendingIntent.FLAG_UPDATE_CURRENT);
80 |
81 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
82 | .setAutoCancel(true).setSmallIcon(R.mipmap.ic_launcher)
83 | .setContentTitle(context.getString(R.string.app_name))
84 | .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
85 | .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
86 | .setContentText(message).setContentIntent(contentIntent);
87 |
88 | NotificationManager mNotificationManager = (NotificationManager) context
89 | .getSystemService(Context.NOTIFICATION_SERVICE);
90 | mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/domain/crypto/AndroidMSecureKeyStore.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.domain.crypto;
2 |
3 | import android.os.Build;
4 | import android.security.keystore.KeyGenParameterSpec;
5 | import android.security.keystore.KeyProperties;
6 | import android.support.annotation.RequiresApi;
7 |
8 | import java.io.IOException;
9 | import java.security.GeneralSecurityException;
10 | import java.security.Key;
11 | import java.security.KeyPairGenerator;
12 | import java.security.KeyStore;
13 |
14 | import javax.crypto.KeyGenerator;
15 |
16 | /**
17 | * Implement the SecureKeyStore using the Android native KeyStore APIs. This requires Android M and later versions, as from this version, the Android keyStore supports generating AES keys.
18 | */
19 | @RequiresApi(Build.VERSION_CODES.M)
20 | public class AndroidMSecureKeyStore extends SecureKeyStoreImpl implements SecureKeyStore {
21 |
22 |
23 | @Override
24 | public String getSupportedAESMode() {
25 | return ALG_AES_GCM_NOPADDING;
26 | }
27 |
28 | @Override
29 | public String getSupportedRSAMode() {
30 | return ALG_RSA_ECB_PCKS1Padding;
31 | }
32 |
33 | @Override
34 | public boolean hasSecretKey(String keyAlias) throws GeneralSecurityException, IOException {
35 | KeyStore ks = loadKeyStore();
36 | return ks.containsAlias(keyAlias);
37 | }
38 |
39 | @Override
40 | public Key getSecretKey(String keyAlias) throws GeneralSecurityException, IOException {
41 | KeyStore ks = loadKeyStore();
42 | return loadKeyStore().getKey(keyAlias, null);
43 | }
44 |
45 | // tag::generateAESKey[]
46 | /**
47 | * Generate the AES key for encryption/decryption. The key will be 128bit and it can only be used with AES/GCM/NoPadding mode.
48 | * @param keyAlias the key alias
49 | * @throws GeneralSecurityException
50 | * @throws IOException
51 | */
52 | @Override
53 | public void generateAESKey(String keyAlias) throws GeneralSecurityException, IOException {
54 | KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
55 | //TODO: further control if user authentication is required for accessing the keys
56 | KeyGenParameterSpec keyGenerationParameters = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
57 | .setKeySize(AES_KEYSIZE_128)
58 | .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
59 | .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
60 | .setRandomizedEncryptionRequired(true)
61 | .build();
62 | keyGenerator.init(keyGenerationParameters);
63 | keyGenerator.generateKey();
64 | }
65 | // end::generateAESKey[]
66 |
67 | /**
68 | * Generate a public/private key pair for encryption/decryption purpose only. The key will be 2048bit and it can only be used with RSA/ECB/PKCS1Padding mode.
69 | * @param keyAlias
70 | * @throws GeneralSecurityException
71 | * @throws IOException
72 | */
73 | @Override
74 | public void generatePrivateKeyPair(String keyAlias) throws GeneralSecurityException, IOException {
75 | KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
76 | KeyGenParameterSpec keyPairGenerationParameters = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_DECRYPT)
77 | .setKeySize(RSA_KEY_SIZE)
78 | .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
79 | .build();
80 | keyPairGenerator.initialize(keyPairGenerationParameters);
81 | keyPairGenerator.generateKeyPair();
82 | }
83 |
84 | @Override
85 | public boolean hasKeyPair(String keyAlias) throws GeneralSecurityException, IOException {
86 | KeyStore ks = loadKeyStore();
87 | return ks.containsAlias(keyAlias);
88 | }
89 |
90 | @Override
91 | public void deleteKey(String keyAlias) throws GeneralSecurityException, IOException {
92 | if (hasSecretKey(keyAlias)) {
93 | KeyStore ks = loadKeyStore();
94 | ks.deleteEntry(keyAlias);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/aerogear/androidshowcase/di/SecureApplicationTestModule.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 |
6 | import com.aerogear.androidshowcase.domain.crypto.AesCrypto;
7 | import com.aerogear.androidshowcase.domain.crypto.PreAndroidMSecureKeyStore;
8 | import com.aerogear.androidshowcase.domain.crypto.RsaCrypto;
9 | import com.aerogear.androidshowcase.domain.crypto.SecureKeyStore;
10 | import com.aerogear.androidshowcase.domain.repositories.NoteRepository;
11 | import com.aerogear.androidshowcase.domain.repositories.NoteRepositoryImpl;
12 | import com.aerogear.androidshowcase.domain.store.NoteDataStore;
13 | import com.aerogear.androidshowcase.domain.store.NoteDataStoreFactory;
14 | import com.aerogear.androidshowcase.domain.store.SecureFileNoteStore;
15 | import com.aerogear.androidshowcase.domain.store.sqlite.SqliteNoteStore;
16 | import com.aerogear.androidshowcase.features.authentication.providers.KeycloakAuthenticateProviderImpl;
17 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
18 |
19 | import org.aerogear.mobile.auth.AuthService;
20 | import org.aerogear.mobile.auth.configuration.AuthServiceConfiguration;
21 | import org.aerogear.mobile.core.MobileCore;
22 | import org.aerogear.mobile.security.SecurityService;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | import javax.inject.Named;
28 | import javax.inject.Singleton;
29 |
30 | import dagger.Module;
31 | import dagger.Provides;
32 |
33 | import static org.mockito.Mockito.mock;
34 |
35 | /**
36 | * Define the DI providers for the tests here.
37 | */
38 | @Module
39 | public class SecureApplicationTestModule {
40 |
41 |
42 | @Provides @Singleton
43 | Context provideApplicationContext() {
44 | return InstrumentationRegistry.getTargetContext();
45 | }
46 |
47 | @Provides @Singleton @Named("fileStore")
48 | NoteDataStore providesNoteDataStore(Context context, AesCrypto aesCrypto) {
49 | return new SecureFileNoteStore(context, aesCrypto);
50 | }
51 |
52 | @Provides @Singleton @Named("sqliteStore")
53 | NoteDataStore providesSqliteNoteDataStore(Context context, RsaCrypto rsaCrypto) {
54 | return new SqliteNoteStore(context, rsaCrypto);
55 | }
56 |
57 | @Provides @Singleton
58 | NoteDataStoreFactory provideNoteDataStoreFactory(Context context, @Named("fileStore") NoteDataStore fileStore, @Named("sqliteStore") NoteDataStore sqlStore) {
59 | List stores = new ArrayList();
60 | stores.add(fileStore);
61 | stores.add(sqlStore);
62 | return new NoteDataStoreFactory(context, stores);
63 | }
64 |
65 | @Provides @Singleton NoteRepository provideNoteRepository(NoteRepositoryImpl noteRepo) {
66 | return noteRepo;
67 | }
68 |
69 | @Provides @Singleton
70 | OpenIDAuthenticationProvider provideAuthProvider() {
71 | return mock(KeycloakAuthenticateProviderImpl.class);
72 | }
73 |
74 | @Provides @Singleton
75 | SecureKeyStore provideSecureKeyStore(Context context) {
76 | return new PreAndroidMSecureKeyStore(context);
77 | }
78 |
79 | @Provides @Singleton
80 | AesCrypto provideAesGcmCrypto(SecureKeyStore keyStore) {
81 | return new AesCrypto(keyStore);
82 | }
83 |
84 | @Provides @Singleton
85 | MobileCore provideMobileCore(Context context) {
86 | MobileCore mobileCore = MobileCore.getInstance();
87 | return mobileCore;
88 | }
89 |
90 | @Provides @Singleton
91 | SecurityService securityService(Context context, MobileCore mobileCore) {
92 | return new SecurityService();
93 | }
94 |
95 | @Provides @Singleton
96 | AuthService provideAuthService(Context context, MobileCore mobileCore) {
97 | AuthServiceConfiguration authServiceConfig = new AuthServiceConfiguration.AuthConfigurationBuilder()
98 | .withRedirectUri("com.aerogear.androidshowcase:/callback")
99 | .build();
100 | AuthService authService = new AuthService(authServiceConfig);
101 | return authService;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/features/landing/LandingFragment.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.features.landing;
2 |
3 | import android.app.Activity;
4 | import android.databinding.ObservableArrayList;
5 | import android.databinding.ObservableList;
6 | import android.os.Bundle;
7 | import android.support.annotation.ArrayRes;
8 | import android.support.annotation.LayoutRes;
9 | import android.support.annotation.Nullable;
10 | import android.support.annotation.StringRes;
11 | import android.support.v7.widget.LinearLayoutManager;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.text.Html;
14 | import android.view.LayoutInflater;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.widget.TextView;
18 |
19 | import com.aerogear.androidshowcase.BR;
20 | import com.aerogear.androidshowcase.R;
21 | import com.aerogear.androidshowcase.features.landing.presenters.LandingPresenter;
22 | import com.aerogear.androidshowcase.features.landing.views.LandingViewImpl;
23 | import com.aerogear.androidshowcase.mvp.presenters.Presenter;
24 | import com.aerogear.androidshowcase.mvp.views.AppView;
25 | import com.aerogear.androidshowcase.mvp.views.BaseFragment;
26 | import com.github.nitrico.lastadapter.LastAdapter;
27 |
28 | import java.util.Arrays;
29 |
30 | import javax.inject.Inject;
31 |
32 | import butterknife.BindView;
33 | import butterknife.ButterKnife;
34 | import dagger.android.AndroidInjection;
35 |
36 | public class LandingFragment extends BaseFragment {
37 |
38 | private static final String TITLE = "title";
39 | private static final String DESCRIPTION = "description";
40 |
41 | public static final String TAG = "Landing";
42 |
43 | private ObservableList description = new ObservableArrayList<>();
44 |
45 | @Inject
46 | LandingPresenter landingPresenter;
47 |
48 | @BindView(R.id.title)
49 | TextView mTitle;
50 |
51 | @BindView(R.id.description)
52 | RecyclerView mDescription;
53 |
54 | @Nullable
55 | @Override
56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
57 | Bundle savedInstanceState) {
58 |
59 | View view = inflater.inflate(R.layout.fragment_landing, container, false);
60 |
61 | ButterKnife.bind(this, view);
62 |
63 | mDescription.setLayoutManager(new LinearLayoutManager(getActivity()));
64 |
65 | new LastAdapter(description, BR.description)
66 | .map(String.class, R.layout.item_landing)
67 | .into(mDescription);
68 |
69 | return view;
70 | }
71 |
72 | @Override
73 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
74 | super.onViewCreated(view, savedInstanceState);
75 |
76 | @StringRes int titleResId = getArguments().getInt(TITLE);
77 | mTitle.setText(getString(titleResId));
78 |
79 | @ArrayRes int descriptionResId = getArguments().getInt(DESCRIPTION);
80 | String[] descriptionArray = getResources().getStringArray(descriptionResId);
81 | description.addAll(Arrays.asList(descriptionArray));
82 | }
83 |
84 | @Override
85 | public void onAttach(Activity activity) {
86 | AndroidInjection.inject(this);
87 | super.onAttach(activity);
88 | }
89 |
90 | @Override
91 | public void onDetach() {
92 | super.onDetach();
93 | this.landingPresenter = null;
94 | }
95 |
96 | @Override
97 | protected Presenter initPresenter() {
98 | return landingPresenter;
99 | }
100 |
101 | @Override
102 | protected AppView initView() {
103 | return new LandingViewImpl(this) {
104 | };
105 | }
106 |
107 | public static LandingFragment newInstance(@StringRes int titleResId,
108 | @ArrayRes int descriptionResId) {
109 | LandingFragment fragment = new LandingFragment();
110 |
111 | Bundle args = new Bundle();
112 | args.putInt(TITLE, titleResId);
113 | args.putInt(DESCRIPTION, descriptionResId);
114 |
115 | fragment.setArguments(args);
116 |
117 | return fragment;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/aerogear/androidshowcase/di/SecureApplicationModule.java:
--------------------------------------------------------------------------------
1 | package com.aerogear.androidshowcase.di;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.support.annotation.Nullable;
7 |
8 | import com.aerogear.androidshowcase.providers.PushServiceProvider;
9 | import com.aerogear.androidshowcase.domain.crypto.AesCrypto;
10 | import com.aerogear.androidshowcase.domain.crypto.AndroidMSecureKeyStore;
11 | import com.aerogear.androidshowcase.domain.crypto.NullAndroidSecureKeyStore;
12 | import com.aerogear.androidshowcase.domain.crypto.PreAndroidMSecureKeyStore;
13 | import com.aerogear.androidshowcase.domain.crypto.RsaCrypto;
14 | import com.aerogear.androidshowcase.domain.crypto.SecureKeyStore;
15 | import com.aerogear.androidshowcase.features.authentication.providers.KeycloakAuthenticateProviderImpl;
16 | import com.aerogear.androidshowcase.features.authentication.providers.OpenIDAuthenticationProvider;
17 |
18 | import org.aerogear.mobile.auth.AuthService;
19 | import org.aerogear.mobile.auth.configuration.AuthServiceConfiguration;
20 | import org.aerogear.mobile.core.MobileCore;
21 | import org.aerogear.mobile.security.SecurityService;
22 |
23 | import javax.inject.Singleton;
24 |
25 | import dagger.Module;
26 | import dagger.Provides;
27 |
28 | /**
29 | * Define the dependencies for the app. Used by Dagger2 to build dependency graph.
30 | */
31 |
32 | @Module
33 | public class SecureApplicationModule {
34 |
35 | @Provides @Singleton
36 | Context provideApplicationContext(Application app) {
37 | return app;
38 | }
39 |
40 | @Provides @Singleton
41 | SecureKeyStore providesSecureKeyStore(Context context) {
42 | int currentSDKVersion = Build.VERSION.SDK_INT;
43 | //For demo purpose, here we choose different implementation based on the current Android version.
44 | //However, this means is a device is upgrade from pre-Android M to Android M, the keys will be lost.
45 | //So it's best to just choose one implementation, based on the minimum API level requirement of the app.
46 | if ( currentSDKVersion >= Build.VERSION_CODES.M) {
47 | return new AndroidMSecureKeyStore();
48 | } else if (currentSDKVersion >= Build.VERSION_CODES.KITKAT) {
49 | return new PreAndroidMSecureKeyStore(context);
50 | } else {
51 | return new NullAndroidSecureKeyStore();
52 | }
53 | }
54 |
55 | @Provides @Singleton
56 | AesCrypto provideAesCrypto(SecureKeyStore keyStore) {
57 | return new AesCrypto(keyStore);
58 | }
59 |
60 | @Provides @Singleton
61 | RsaCrypto provideRsaCrypto(SecureKeyStore keyStore) {
62 | return new RsaCrypto(keyStore);
63 | }
64 |
65 | @Provides @Singleton
66 | OpenIDAuthenticationProvider provideAuthProvider(KeycloakAuthenticateProviderImpl keycloakClient) {
67 | return keycloakClient;
68 | }
69 |
70 | // tag::securityServiceInit[]
71 | @Provides @Singleton
72 | SecurityService provideSecurityService() {
73 | return new SecurityService();
74 | }
75 | // end::securityServiceInit[]
76 |
77 | @Provides @Singleton @Nullable
78 | PushServiceProvider providePushServiceProvider() {
79 | if (MobileCore.getInstance().getServiceConfigurationsByType("push") == null) {
80 | return null;
81 | }
82 | return PushServiceProvider.getInstance();
83 | }
84 |
85 | // tag::authServiceInit[]
86 | @Provides @Singleton @Nullable
87 | AuthService provideAuthService(Context context) {
88 | MobileCore core = MobileCore.getInstance();
89 | if (core.getServiceConfigurationByType("keycloak") == null ) {
90 | return null;//We are allowing this to be nullable because keycloak is not guaranteed to
91 | //be configured. We are not returning an Optional because we can't guarantee
92 | //Optional is available on Android M and L and we don't want to add Guava.
93 | }
94 | AuthServiceConfiguration authServiceConfig = new AuthServiceConfiguration.AuthConfigurationBuilder()
95 | .withRedirectUri("com.aerogear.androidshowcase:/callback")
96 | .build();
97 | return new AuthService(authServiceConfig);
98 | }
99 | // end::authServiceInit[]
100 | }
101 |
--------------------------------------------------------------------------------