diff --git a/.idea/inspectionProfiles/OPF.xml b/.idea/inspectionProfiles/OPF.xml index 93e193d6..9354b33c 100644 --- a/.idea/inspectionProfiles/OPF.xml +++ b/.idea/inspectionProfiles/OPF.xml @@ -1,165 +1,162 @@ - - + + \ No newline at end of file diff --git a/README.md b/README.md index 69d70dcf..b5180a52 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ provides possibility to use the system push provider for a specific device. - [Download](#user-content-download) - [How To Use](#user-content-how-to-use) - [Using of OPFPushReceiver](#user-content-using-of-opfpushreceiver) +- [Notification payload support](#user-content-notification-payload-support) - [Implemented Push Services](#user-content-implemented-push-services) - [Create Custom Push Provider](#user-content-create-custom-push-provider) - [Comparison of most popular push services](#user-content-comparison-of-most-popular-push-services) @@ -28,7 +29,7 @@ provides possibility to use the system push provider for a specific device. Download [the latest AAR][opfpush-latest-aar] of OPFPush and [the latest JAR][opfutils-latest-jar] of OPFUtils or grab it via Gradle: ```groovy - compile 'org.onepf:opfpush:0.2.3@aar' + compile 'org.onepf:opfpush:0.3.0@aar' compile 'org.onepf:opfutils:0.1.22' ``` @@ -37,7 +38,7 @@ provides possibility to use the system push provider for a specific device. org.onepf opfpush - 0.2.3 + 0.3.0 aar @@ -95,12 +96,18 @@ to implement GCM using OPFPush library. You can use `BroadcastReceiver` instead of `EventListener` for receiving push events. See [the following section][opfpush-receiver-section] +##Notification payload support + +[GCM Notification payload support][gcm-notification-payload-support] was added to the library. +Also we have implemented a similar mechanism for all supported push providers. +See [the following section][opf-notification-payload-support] + ## Implemented Push Services 1. [Google Cloud Messaging][google-cloud-messaging]. Download [the latest AAR][gcm-latest-aar] or grab via Gradle: ```groovy - compile 'org.onepf:opfpush-gcm:0.2.3@aar' + compile 'org.onepf:opfpush-gcm:0.3.0@aar' ``` or Maven: @@ -108,7 +115,7 @@ See [the following section][opfpush-receiver-section] org.onepf opfpush-gcm - 0.2.3 + 0.3.0 aar ``` @@ -118,7 +125,7 @@ See [the following section][opfpush-receiver-section] 2. [Amazon Device Messaging][amazon-device-messaging]. Download [the latest AAR][adm-latest-aar] or grab via Gradle: ```groovy - compile 'org.onepf:opfpush-adm:0.2.3@aar' + compile 'org.onepf:opfpush-adm:0.3.0@aar' ``` or Maven: @@ -126,7 +133,7 @@ See [the following section][opfpush-receiver-section] org.onepf opfpush-adm - 0.2.3 + 0.3.0 aar ``` @@ -136,7 +143,7 @@ See [the following section][opfpush-receiver-section] 3. [Nokia Notifications][nokia-notifications]. Download [the latest AAR][nokia-latest-aar] or grab via Gradle: ```groovy - compile 'org.onepf:opfpush-nokia:0.2.3@aar' + compile 'org.onepf:opfpush-nokia:0.3.0@aar' ``` or Maven: @@ -144,7 +151,7 @@ See [the following section][opfpush-receiver-section] org.onepf opfpush-nokia - 0.2.3 + 0.3.0 aar ``` @@ -160,6 +167,8 @@ To create a custom push provider see [the following section][custom-push-provide | Criteria | GCM | ADM | Nokia Notifications | OPFPush | | :---------------------------------- | :---: | :---: | :-----------------: | :---------: | | Receive messages | + | + | + | + | +| Multiple senders | + | - | + | + | +| Notification payload support | + | - | - | - | | Asynchronous registration and unregistration | - | + | + | + | | Retry register on fail | - | + | + | + | | Retry register on fail after reboot | - | - | - | + | @@ -202,9 +211,11 @@ Take a look at the usage of the OPFPush library in our [sample application][samp [opfpush-gcm]: ./opfpush-providers/gcm [opfpush-adm]: ./opfpush-providers/adm [opfpush-nokia]: ./opfpush-providers/nokia -[opfpush-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-0.2.3.aar -[gcm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-gcm-0.2.3.aar -[adm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-adm-0.2.3.aar -[nokia-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-nokia-0.2.3.aar +[gcm-notification-payload-support]: https://developers.google.com/cloud-messaging/server-ref#notification-payload-support +[opf-notification-payload-support]: https://github.com/onepf/OPFPush/wiki/Notification-payload-support +[opfpush-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-0.3.0.aar +[gcm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-gcm-0.3.0.aar +[adm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-adm-0.3.0.aar +[nokia-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-nokia-0.3.0.aar [easiest-gcm]: https://github.com/onepf/OPFPush/wiki/The-easiest-way-to-implement-GCM [sample]: https://github.com/onepf/OPFPush/tree/master/samples/pushchat diff --git a/opfpush-providers/adm/README.md b/opfpush-providers/adm/README.md index b791e02d..ae2cc32c 100644 --- a/opfpush-providers/adm/README.md +++ b/opfpush-providers/adm/README.md @@ -19,7 +19,7 @@ allprojects { Download [the latest AAR][adm-latest-aar] or grab via Gradle: ```groovy -compile 'org.onepf:opfpush-adm:0.2.3@aar' +compile 'org.onepf:opfpush-adm:0.3.0@aar' ``` or Maven: @@ -27,7 +27,7 @@ or Maven: org.onepf opfpush-adm - 0.2.3 + 0.3.0 aar ``` @@ -35,7 +35,7 @@ or Maven: You can also use JAR dependency. Download [the latest JAR][adm-latest-jar] or grab via Gradle: ```groovy -compile 'org.onepf:opfpush-adm:0.2.3' +compile 'org.onepf:opfpush-adm:0.3.0' ``` or Maven: @@ -43,7 +43,7 @@ or Maven: org.onepf opfpush-adm - 0.2.3 + 0.3.0 ``` @@ -127,5 +127,5 @@ builder.addProviders(new ADMProvider(context)); ``` [1]: https://developer.amazon.com/appsandservices/apis/engage/device-messaging -[adm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-adm-0.2.3.aar -[adm-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-adm-0.2.3.jar \ No newline at end of file +[adm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-adm-0.3.0.aar +[adm-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-adm-0.3.0.jar \ No newline at end of file diff --git a/opfpush-providers/adm/build.gradle b/opfpush-providers/adm/build.gradle index 5f906b11..51744775 100644 --- a/opfpush-providers/adm/build.gradle +++ b/opfpush-providers/adm/build.gradle @@ -21,17 +21,19 @@ android { defaultConfig { minSdkVersion 15 targetSdkVersion 22 - versionName "0.2.3" + versionName "0.3.0" } } dependencies { testCompile 'junit:junit:4.12' + //noinspection NewerVersionAvailable testCompile 'org.robolectric:robolectric:2.4' androidTestCompile 'junit:junit:4.12' + //noinspection NewerVersionAvailable androidTestCompile 'org.robolectric:robolectric:2.4' - compile 'org.onepf:opfpush:0.2.3@aar' + compile 'org.onepf:opfpush:0.3.0@aar' provided 'com.amazon:amazon-device-messaging:1.0.1' provided 'com.android.support:support-annotations:19.1.0' diff --git a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProvider.java b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProvider.java index 0f4de5b2..53c08df9 100644 --- a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProvider.java +++ b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProvider.java @@ -23,6 +23,7 @@ import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfpush.pushprovider.PushProvider; import org.onepf.opfutils.OPFLog; @@ -51,6 +52,17 @@ public ADMProvider(@NonNull final Context context) { } } + public ADMProvider(@NonNull final Context context, + @NonNull final NotificationMaker notificationMaker) { + if (Build.MANUFACTURER.equals(AMAZON_MANUFACTURER)) { + OPFLog.d("It's an Amazon device."); + provider = new ADMProviderImpl(context.getApplicationContext(), notificationMaker); + } else { + OPFLog.d("It's no an Amazon device."); + provider = new ADMProviderStub(); + } + } + @Override public void register() { provider.register(); @@ -90,6 +102,12 @@ public String getHostAppPackage() { return provider.getHostAppPackage(); } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + return provider.getNotificationMaker(); + } + @Override public void checkManifest(@Nullable final CheckManifestHandler checkManifestHandler) { provider.checkManifest(checkManifestHandler); diff --git a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderImpl.java b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderImpl.java index da5589c6..3ea372f9 100644 --- a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderImpl.java +++ b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderImpl.java @@ -31,6 +31,7 @@ import org.onepf.opfpush.BasePushProvider; import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfpush.utils.CheckUtils; import org.onepf.opfutils.OPFLog; @@ -52,15 +53,17 @@ class ADMProviderImpl extends BasePushProvider { @NonNull - private final ADM adm; + private final ADM adm = new ADM(getContext().getApplicationContext()); @NonNull - private final PreferencesProvider preferencesProvider; + private final PreferencesProvider preferencesProvider = PreferencesProvider.getInstance(getContext()); public ADMProviderImpl(@NonNull final Context context) { super(context, PROVIDER_NAME, KINDLE_STORE_APP_PACKAGE); - adm = new ADM(context.getApplicationContext()); - preferencesProvider = PreferencesProvider.getInstance(getContext()); + } + + public ADMProviderImpl(@NonNull final Context context, @NonNull final NotificationMaker notificationMaker) { + super(context, PROVIDER_NAME, KINDLE_STORE_APP_PACKAGE, notificationMaker); } @Override diff --git a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderStub.java b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderStub.java index 879bde25..5f9d9009 100644 --- a/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderStub.java +++ b/opfpush-providers/adm/src/main/java/org/onepf/opfpush/adm/ADMProviderStub.java @@ -16,11 +16,13 @@ package org.onepf.opfpush.adm; +import android.content.Context; +import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfpush.pushprovider.PushProvider; import org.onepf.opfutils.OPFLog; @@ -78,6 +80,23 @@ public String getHostAppPackage() { return null; } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + OPFLog.logMethod(); + return new NotificationMaker() { + @Override + public boolean needShowNotification(@NonNull Bundle bundle) { + return false; + } + + @Override + public void showNotification(@NonNull Context context, @NonNull Bundle bundle) { + //nothing + } + }; + } + @Override public void checkManifest(@Nullable final CheckManifestHandler checkManifestHandler) { OPFLog.logMethod(); diff --git a/opfpush-providers/gcm/README.md b/opfpush-providers/gcm/README.md index 6576e7a8..5dc93b5f 100644 --- a/opfpush-providers/gcm/README.md +++ b/opfpush-providers/gcm/README.md @@ -4,7 +4,7 @@ Download [the latest AAR][gcm-latest-aar] or grab via Gradle: ```groovy -compile 'org.onepf:opfpush-gcm:0.2.3@aar' +compile 'org.onepf:opfpush-gcm:0.3.0@aar' ``` or Maven: @@ -12,7 +12,7 @@ or Maven: org.onepf opfpush-gcm - 0.2.3 + 0.3.0 aar ``` @@ -20,7 +20,7 @@ or Maven: You can also use JAR dependency. Download [the latest JAR][gcm-latest-jar] or grab via Gradle: ```groovy -compile 'org.onepf:opfpush-gcm:0.2.3' +compile 'org.onepf:opfpush-gcm:0.3.0' ``` or Maven: @@ -28,7 +28,7 @@ or Maven: org.onepf opfpush-gcm - 0.2.3 + 0.3.0 ``` @@ -49,16 +49,13 @@ also add the following receiver: ```xml - - - - - - + + + ``` @@ -67,17 +64,27 @@ If you use JAR dependency, you also must add to your application AndroidManifest ```xml - + android:name=".GCMService" + android:exported="false"> + + + + + + + + + + - ``` @@ -91,5 +98,5 @@ builder.addProviders(new GCMProvider(context, GCM_SENDER_ID)); ``` [1]: https://developer.android.com/google/gcm/index.html -[gcm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-gcm-0.2.3.aar -[gcm-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-gcm-0.2.3.jar \ No newline at end of file +[gcm-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-gcm-0.3.0.aar +[gcm-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-gcm-0.3.0.jar \ No newline at end of file diff --git a/opfpush-providers/gcm/build.gradle b/opfpush-providers/gcm/build.gradle index 632b5246..d67d9db8 100644 --- a/opfpush-providers/gcm/build.gradle +++ b/opfpush-providers/gcm/build.gradle @@ -21,19 +21,21 @@ android { defaultConfig { minSdkVersion 15 targetSdkVersion 22 - versionName "0.2.3" + versionName "0.3.0" } } dependencies { testCompile 'junit:junit:4.12' + //noinspection NewerVersionAvailable testCompile 'org.robolectric:robolectric:2.4' androidTestCompile 'junit:junit:4.12' + //noinspection NewerVersionAvailable androidTestCompile 'org.robolectric:robolectric:2.4' - compile 'org.onepf:opfpush:0.2.3@aar' - //noinspection ObsoleteGradleDependency - compile 'com.google.android.gms:play-services-gcm:7.3.0' + compile 'org.onepf:opfpush:0.3.0@aar' + //noinspection NewerVersionAvailable + compile 'com.google.android.gms:play-services-gcm:7.5.0' provided 'com.android.support:support-annotations:19.1.0' //noinspection NewerVersionAvailable diff --git a/opfpush-providers/gcm/src/main/AndroidManifest.xml b/opfpush-providers/gcm/src/main/AndroidManifest.xml index fcc69cf6..e8c08e0c 100644 --- a/opfpush-providers/gcm/src/main/AndroidManifest.xml +++ b/opfpush-providers/gcm/src/main/AndroidManifest.xml @@ -22,7 +22,19 @@ + android:exported="false"> + + + + + + + + + + org.onepf opfpush-nokia - 0.2.3 + 0.3.0 aar ``` @@ -35,7 +35,7 @@ or Maven: You can also use JAR dependency. Download [the latest JAR][nokia-latest-jar] or grab via Gradle: ```groovy -compile 'org.onepf:opfpush-nokia:0.2.3' +compile 'org.onepf:opfpush-nokia:0.3.0' ``` or Maven: @@ -43,7 +43,7 @@ or Maven: org.onepf opfpush-nokia - 0.2.3 + 0.3.0 ``` @@ -99,5 +99,5 @@ builder.addProviders(new NokiaNotificationsProvider(context, NOKIA_NOTIFICATION_ ``` [Nokia Notifications Page]: http://developer.nokia.com/resources/library/nokia-x/nokia-notifications.html -[nokia-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-nokia-0.2.3.aar -[nokia-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.2.3/opfpush-nokia-0.2.3.jar +[nokia-latest-aar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-nokia-0.3.0.aar +[nokia-latest-jar]: https://github.com/onepf/OPFPush/releases/download/v0.3.0/opfpush-nokia-0.3.0.jar diff --git a/opfpush-providers/nokia/build.gradle b/opfpush-providers/nokia/build.gradle index 4a1cd61a..5d755800 100644 --- a/opfpush-providers/nokia/build.gradle +++ b/opfpush-providers/nokia/build.gradle @@ -21,12 +21,12 @@ android { defaultConfig { minSdkVersion 15 targetSdkVersion 22 - versionName "0.2.3" + versionName "0.3.0" } } dependencies { - compile 'org.onepf:opfpush:0.2.3@aar' + compile 'org.onepf:opfpush:0.3.0@aar' compile 'com.nokia:push:1.0' provided 'com.android.support:support-annotations:19.1.0' diff --git a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProvider.java b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProvider.java index 7a0fb0a8..7bd23182 100644 --- a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProvider.java +++ b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProvider.java @@ -23,6 +23,7 @@ import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfutils.OPFLog; import static org.onepf.opfpush.nokia.NokiaPushConstants.NOKIA_MANUFACTURER; @@ -51,6 +52,18 @@ public NokiaNotificationsProvider(@NonNull final Context context, } } + public NokiaNotificationsProvider(@NonNull final Context context, + @NonNull final NotificationMaker notificationMaker, + @NonNull final String... sendersIds) { + if (Build.MANUFACTURER.equals(NOKIA_MANUFACTURER)) { + OPFLog.d("It's a Nokia device."); + provider = new NokiaNotificationsProviderImpl(context, notificationMaker, sendersIds); + } else { + OPFLog.d("It's no a Nokia device."); + provider = new NokiaNotificationsProviderStub(); + } + } + @Override public void register() { provider.register(); @@ -90,6 +103,12 @@ public String getHostAppPackage() { return provider.getHostAppPackage(); } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + return provider.getNotificationMaker(); + } + @Override public void checkManifest(@Nullable final CheckManifestHandler checkManifestHandler) { provider.checkManifest(checkManifestHandler); diff --git a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderImpl.java b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderImpl.java index 92e2aa38..45da5730 100644 --- a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderImpl.java +++ b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderImpl.java @@ -27,6 +27,7 @@ import org.onepf.opfpush.BasePushProvider; import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfpush.utils.CheckUtils; import org.onepf.opfutils.OPFLog; @@ -55,6 +56,13 @@ public NokiaNotificationsProviderImpl(@NonNull final Context context, this.sendersIds = sendersIds; } + public NokiaNotificationsProviderImpl(@NonNull final Context context, + @NonNull final NotificationMaker notificationMaker, + @NonNull final String... sendersIds) { + super(context, PROVIDER_NAME, NOKIA_STORE_APP_PACKAGE, notificationMaker); + this.sendersIds = sendersIds; + } + @NonNull @Override public AvailabilityResult getAvailabilityResult() { diff --git a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderStub.java b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderStub.java index 7182e59a..b94e4a3b 100644 --- a/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderStub.java +++ b/opfpush-providers/nokia/src/main/java/org/onepf/opfpush/nokia/NokiaNotificationsProviderStub.java @@ -17,11 +17,13 @@ package org.onepf.opfpush.nokia; import android.content.Context; +import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfutils.OPFLog; import static org.onepf.opfpush.nokia.NokiaPushConstants.PROVIDER_NAME; @@ -100,6 +102,23 @@ public String getHostAppPackage() { return null; } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + OPFLog.logMethod(); + return new NotificationMaker() { + @Override + public boolean needShowNotification(@NonNull Bundle bundle) { + return false; + } + + @Override + public void showNotification(@NonNull Context context, @NonNull Bundle bundle) { + //nothing + } + }; + } + @Override public void checkManifest(@Nullable final CheckManifestHandler checkManifestHandler) { OPFLog.logMethod(); diff --git a/opfpush/build.gradle b/opfpush/build.gradle index a9bc40a6..94aadce3 100644 --- a/opfpush/build.gradle +++ b/opfpush/build.gradle @@ -32,7 +32,7 @@ android { defaultConfig { minSdkVersion 15 targetSdkVersion 22 - versionName "0.2.3" + versionName "0.3.0" } } diff --git a/opfpush/src/main/java/org/onepf/opfpush/BasePushProvider.java b/opfpush/src/main/java/org/onepf/opfpush/BasePushProvider.java index c05d3ece..cf7f0f6f 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/BasePushProvider.java +++ b/opfpush/src/main/java/org/onepf/opfpush/BasePushProvider.java @@ -23,6 +23,8 @@ import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; +import org.onepf.opfpush.notification.OPFNotificationMaker; import org.onepf.opfpush.pushprovider.PushProvider; import org.onepf.opfpush.utils.CheckUtils; import org.onepf.opfutils.OPFUtils; @@ -51,8 +53,11 @@ public abstract class BasePushProvider implements PushProvider { @NonNull private final String hostAppPackage; + @NonNull + private final NotificationMaker notificationMaker; + /** - * Creates a push provider. + * Creates a push provider. The created provider uses {@link OPFNotificationMaker}. * * @param context The {@link android.content.Context} instance. * @param name The name of the provider. @@ -62,9 +67,26 @@ public abstract class BasePushProvider implements PushProvider { protected BasePushProvider(@NonNull final Context context, @NonNull final String name, @NonNull final String hostAppPackage) { + this(context, name, hostAppPackage, new OPFNotificationMaker()); + } + + /** + * Creates a push provider. + * + * @param context The {@link android.content.Context} instance. + * @param name The name of the provider. + * @param hostAppPackage The package of the application that handle push messages from the server + * and deliver it to the user application. + * @param notificationMaker The {@link NotificationMaker} instance. + */ + protected BasePushProvider(@NonNull final Context context, + @NonNull final String name, + @NonNull final String hostAppPackage, + @NonNull final NotificationMaker notificationMaker) { this.appContext = context.getApplicationContext(); this.name = name; this.hostAppPackage = hostAppPackage; + this.notificationMaker = notificationMaker; } @NonNull @@ -116,6 +138,12 @@ public String getHostAppPackage() { return hostAppPackage; } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + return notificationMaker; + } + @Override public String toString() { return name + "(hostAppPackage='" + hostAppPackage + ')'; diff --git a/opfpush/src/main/java/org/onepf/opfpush/BootCompleteReceiver.java b/opfpush/src/main/java/org/onepf/opfpush/BootCompleteReceiver.java index e3f0de5d..514db879 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/BootCompleteReceiver.java +++ b/opfpush/src/main/java/org/onepf/opfpush/BootCompleteReceiver.java @@ -42,7 +42,7 @@ public void onReceive(@NonNull final Context context, @NonNull final Intent inte OPFLog.i("Helper is registered"); if (isAndroidIDChanged(context)) { OPFLog.i("Android ID changed."); - helper.onNeedRetryRegister(); + helper.onNeedRetryRegistration(); } else { OPFLog.i("Android ID hasn't been changed"); } diff --git a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelper.java b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelper.java index 46876c71..066f58c1 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelper.java +++ b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelper.java @@ -119,6 +119,11 @@ private void sendMessage(@NonNull final Message message) { */ public abstract boolean isRegistered(); + /** + * Is invoked if registration must be retried. (For example if registration id is expired). + */ + public abstract void onNeedRetryRegistration(); + /** * Returns {@code true} if the registration operation is being performed at the moment. * @@ -136,8 +141,6 @@ private void sendMessage(@NonNull final Message message) { abstract void unregister(@NonNull final String providerName); - abstract void onNeedRetryRegister(); - abstract void registerNextAvailableProvider(@Nullable final String prevProviderName); @Nullable diff --git a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperImpl.java b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperImpl.java index 920c79b9..d7eedb05 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperImpl.java +++ b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperImpl.java @@ -19,6 +19,8 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; @@ -32,6 +34,7 @@ import org.onepf.opfpush.model.PushError; import org.onepf.opfpush.model.State; import org.onepf.opfpush.model.UnrecoverablePushError; +import org.onepf.opfpush.notification.NotificationMaker; import org.onepf.opfpush.pushprovider.PushProvider; import org.onepf.opfutils.OPFChecks; import org.onepf.opfutils.OPFLog; @@ -277,6 +280,18 @@ public boolean isRegistering() { return settings.getState() == REGISTERING; } + @Override + public void onNeedRetryRegistration() { + OPFLog.logMethod(); + OPFLog.d("Current provider : " + currentProvider); + settings.clear(); + if (currentProvider != null) { + currentProvider.onRegistrationInvalid(); + currentProvider = null; + } + register(); + } + @NonNull @Override public String toString() { @@ -361,18 +376,6 @@ void unregister(@NonNull final String providerName) { } } - @Override - void onNeedRetryRegister() { - OPFLog.logMethod(); - OPFLog.d("Current provider : " + currentProvider); - settings.clear(); - if (currentProvider != null) { - currentProvider.onRegistrationInvalid(); - currentProvider = null; - } - register(); - } - @SuppressWarnings("PMD.OneDeclarationPerLine") @Override void registerNextAvailableProvider(@Nullable final String prevProviderName) { @@ -685,6 +688,8 @@ private boolean isOPFReceiverRegistered() { @SuppressWarnings("UnusedDeclaration") private final class ReceivedMessageHandlerImpl implements ReceivedMessageHandler { + private final Handler handler = new Handler(Looper.getMainLooper()); + /** * A push provider calls this method when a new message is received. * @@ -697,7 +702,21 @@ public void onMessage(@NonNull final String providerName, OPFLog.logMethod(providerName); if (currentProvider != null && providerName.equals(currentProvider.getName())) { settings.saveState(REGISTERED); - eventListenerWrapper.onMessage(appContext, providerName, extras); + + //noinspection InnerClassTooDeeplyNested + handler.post(new Runnable() { + @Override + public void run() { + //All operations with NotificationMaker should are performed in the main thread. + //It saves users from having to make thread safe NotificationMaker. + final NotificationMaker notificationMaker = currentProvider.getNotificationMaker(); + if (extras != null && notificationMaker.needShowNotification(extras)) { + notificationMaker.showNotification(appContext, extras); + } else { + eventListenerWrapper.onMessage(appContext, providerName, extras); + } + } + }); } else { OPFLog.w("Ignore onMessage from provider " + providerName + ". Current provider is " + currentProvider); diff --git a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperStub.java b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperStub.java index 06a31b8d..3f7e138b 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperStub.java +++ b/opfpush/src/main/java/org/onepf/opfpush/OPFPushHelperStub.java @@ -82,6 +82,11 @@ public boolean isRegistering() { return false; } + @Override + public void onNeedRetryRegistration() { + OPFLog.logMethod(); + } + @Override void init(@NonNull final Configuration initialConfiguration) { OPFLog.logMethod(initialConfiguration); @@ -108,11 +113,6 @@ void unregister(@NonNull final String providerName) { OPFLog.logMethod(providerName); } - @Override - void onNeedRetryRegister() { - OPFLog.logMethod(); - } - @Override void registerNextAvailableProvider(@Nullable final String prevProviderName) { OPFLog.logMethod(prevProviderName); diff --git a/opfpush/src/main/java/org/onepf/opfpush/PackageChangeReceiver.java b/opfpush/src/main/java/org/onepf/opfpush/PackageChangeReceiver.java index 038400f0..131f9dc1 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/PackageChangeReceiver.java +++ b/opfpush/src/main/java/org/onepf/opfpush/PackageChangeReceiver.java @@ -50,7 +50,7 @@ public void onReceive(@NonNull final Context context, @NonNull final Intent inte } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action) && context.getPackageName().equals(getAppPackage(intent))) { OPFLog.d("Application updated."); - helper.onNeedRetryRegister(); + helper.onNeedRetryRegistration(); } } diff --git a/opfpush/src/main/java/org/onepf/opfpush/model/NotificationPayload.java b/opfpush/src/main/java/org/onepf/opfpush/model/NotificationPayload.java new file mode 100644 index 00000000..ec19ebbf --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/model/NotificationPayload.java @@ -0,0 +1,270 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.model; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +/** + * Notification model class. It has the same fields as the {@code notification} parameter of a GCM downstream message. + * https://developers.google.com/cloud-messaging/server-ref#notification-payload-support + * + * @author Roman Savin + * @since 23.06.2015 + */ +@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") +public final class NotificationPayload { + + @NonNull + private final String title; + + @NonNull + private final String icon; + + @Nullable + private final String body; + + @Nullable + private final String sound; + + @Nullable + private final String tag; + + @Nullable + private final String color; + + @Nullable + private final String clickAction; + + private NotificationPayload(@NonNull final String title, + @NonNull final String icon, + @Nullable final String body, + @Nullable final String sound, + @Nullable final String tag, + @Nullable final String color, + @Nullable final String clickAction) { + this.title = title; + this.icon = icon; + this.body = body; + this.sound = sound; + this.tag = tag; + this.color = color; + this.clickAction = clickAction; + } + + /** + * Returns the field that indicates notification title. + * + * @return The field that indicates notification title. + */ + @NonNull + public String getTitle() { + return title; + } + + /** + * Returns the field that indicates notification icon. + * + * @return The field that indicates notification icon. + */ + @NonNull + public String getIcon() { + return icon; + } + + /** + * Returns the field that indicates notification body text. + * + * @return The field that indicates notification body text. + */ + @Nullable + public String getBody() { + return body; + } + + /** + * Returns the field that indicates sound to be played. + * + * @return The field that indicates sound to be played. Supports only {@code default} currently. + */ + @Nullable + public String getSound() { + return sound; + } + + /** + * Indicates whether each notification message results in a new entry on the notification center on Android. + * If not set, each request creates a new notification. If set, and a notification with the same tag is already being shown, + * the new notification replaces the existing one in notification center. + * + * @return Notification tag. + */ + @Nullable + public String getTag() { + return tag; + } + + /** + * Returns the field that indicates color of the icon, expressed in #rrggbb format. + * + * @return The field that indicates color of the icon, expressed in #rrggbb format. + */ + @Nullable + public String getColor() { + return color; + } + + /** + * The action associated with a user click on the notification. + * + * @return The action associated with a user click on the notification. + */ + @Nullable + public String getClickAction() { + return clickAction; + } + + @Override + public String toString() { + return "NotificationPayload : {" + + " title : " + title + + ", icon : " + icon + + ", body : " + body + + ", sound : " + sound + + ", tag : " + tag + + ", color : " + color + + ", clickAction : " + clickAction + + "}"; + } + + public static final class Builder { + + @Nullable + private String title; + + @Nullable + private String icon; + + @Nullable + private String body; + + @Nullable + private String sound; + + @Nullable + private String tag; + + @Nullable + private String color; + + @Nullable + private String clickAction; + + /** + * Indicates notification title. + * + * @param title Notification title. Required parameter. + * @return Builder instance. + */ + public Builder setTitle(@NonNull final String title) { + this.title = title; + return this; + } + + /** + * Indicates notification icon. Sets value to myicon for drawable resource myicon.png. Required parameter. + * + * @param icon Notification icon. Required parameter. + * @return Builder instance. + */ + public Builder setIcon(@NonNull final String icon) { + this.icon = icon; + return this; + } + + /** + * Indicates notification body text. + * + * @param body Notification body text. Optional parameter. + * @return Builder instance. + */ + public Builder setBody(@Nullable final String body) { + this.body = body; + return this; + } + + /** + * Indicates sound to be played. Supports only default currently. + * + * @param sound Notification sound. Optional parameter. + * @return Builder instance. + */ + public Builder setSound(@Nullable final String sound) { + this.sound = sound; + return this; + } + + /** + * Indicates whether each notification message results in a new entry on the notification center on Android. + * If not set, each request creates a new notification. If set, and a notification with the same tag is already + * being shown, the new notification replaces the existing one in notification center. + * + * @param tag Notification tag. Optional parameter. + * @return Builder instance. + */ + public Builder setTag(@Nullable final String tag) { + this.tag = tag; + return this; + } + + /** + * Indicates color of the icon, expressed in #rrggbb format. + * + * @param color Indicates color of the icon, expressed in #rrggbb format. + * @return Builder instance. + */ + public Builder setColor(@Nullable final String color) { + this.color = color; + return this; + } + + /** + * The action associated with a user click on the notification. + * + * @param clickAction The action associated with a user click on the notification. + * @return Builder instance. + */ + public Builder setClickAction(@Nullable final String clickAction) { + this.clickAction = clickAction; + return this; + } + + @SuppressWarnings("PMD.AccessorClassGeneration") + public NotificationPayload build() { + if (TextUtils.isEmpty(title)) { + throw new IllegalArgumentException("Title can't be empty"); + } + + if (TextUtils.isEmpty(icon)) { + throw new IllegalArgumentException("Icon can't be empty"); + } + + return new NotificationPayload(title, icon, body, sound, tag, color, clickAction); + } + } +} diff --git a/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationMaker.java b/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationMaker.java new file mode 100644 index 00000000..0a8606d0 --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationMaker.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.notification; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; + +/** + * The interface intended to create notifications from bundle data. + * + * @author Roman Savin + * @since 23.06.2015 + */ +public interface NotificationMaker { + + /** + * Returns true if a notification must be shown for the bundle. False otherwise. + * + * @param bundle The bundle received from a push provider. + * @return Returns true if a notification should be shown for the bundle. False otherwise. + */ + boolean needShowNotification(@NonNull final Bundle bundle); + + /** + * Shows notification using bundle data. + * + * @param context The Context instance. + * @param bundle Received data. + */ + void showNotification(@NonNull final Context context, @NonNull final Bundle bundle); +} diff --git a/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationPreparer.java b/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationPreparer.java new file mode 100644 index 00000000..5ebed29a --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/notification/NotificationPreparer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.notification; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import org.onepf.opfpush.model.NotificationPayload; + +/** + * The interface intended to define preparing an {@link NotificationPayload} instance from the {@code bundle}. + * + * @author Roman Savin + * @since 23.06.2015 + */ +public interface NotificationPreparer { + + /** + * Converts the {@code bundle} to the {@code NotificationPayload} instance. + * + * @param bundle The data for converting. + * @return The prepared {@link NotificationPayload} instance. + */ + @Nullable + NotificationPayload prepare(@NonNull final Bundle bundle); +} diff --git a/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationMaker.java b/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationMaker.java new file mode 100644 index 00000000..51c88951 --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationMaker.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.notification; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import org.onepf.opfpush.utils.NotificationUtils; +import org.onepf.opfutils.OPFLog; +import org.onepf.opfutils.OPFUtils; + +/** + * The default implementation of the {@link NotificationMaker} interface. + * It shows a notification if the {@code bundle} contains an extra by the "opf_notification" key and it is "true" String. + * It uses the {@link NotificationUtils#showNotification(Context, Bundle, NotificationPreparer)} method for showing a notification. + * + * @author Roman Savin + * @since 23.06.2015 + */ +public final class OPFNotificationMaker implements NotificationMaker { + + private static final String SHOW_NOTIFICATION_KEY = "opf_notification"; + + @NonNull + private NotificationPreparer notificationPreparer; + + /** + * Default constructor. The {@link OPFNotificationPreparer} instance is used as {code notificationPreparer} by default. + */ + public OPFNotificationMaker() { + this(new OPFNotificationPreparer()); + } + + /** + * Constructor with custom {@code notificationPreparer}. + * + * @param notificationPreparer The notification preparer which converts {@code bundle} + * to a {@link org.onepf.opfpush.model.NotificationPayload} instance. + * It can be useful for implementing custom converting from {@link Bundle} + * to {@link org.onepf.opfpush.model.NotificationPayload}. + */ + public OPFNotificationMaker(@NonNull final NotificationPreparer notificationPreparer) { + this.notificationPreparer = notificationPreparer; + } + + /** + * Returns {@code true} if the {@code bundle} contains the “true” value by the “opf_notification” key. + * + * @param bundle The bundle received from a push provider. + * @return {@code true} if the {@code bundle} contains the “true” value by the “opf_notification” key. False otherwise. + */ + @Override + public boolean needShowNotification(@NonNull final Bundle bundle) { + OPFLog.logMethod(OPFUtils.toString(bundle)); + return Boolean.parseBoolean(bundle.getString(SHOW_NOTIFICATION_KEY)); + } + + /** + * Shows a notification using the {@link NotificationUtils#showNotification(Context, Bundle, NotificationPreparer)} method. + * + * @param context The Context instance. + * @param bundle The bundle received from a push provider. + */ + @Override + public void showNotification(@NonNull final Context context, @NonNull final Bundle bundle) { + OPFLog.logMethod(context, OPFUtils.toString(bundle)); + NotificationUtils.showNotification(context, bundle, notificationPreparer); + } +} diff --git a/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationPreparer.java b/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationPreparer.java new file mode 100644 index 00000000..42494783 --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/notification/OPFNotificationPreparer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.notification; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import org.onepf.opfpush.model.NotificationPayload; +import org.onepf.opfutils.OPFLog; +import org.onepf.opfutils.OPFUtils; + +/** + * The default implementation of the {@link NotificationPreparer} interface. + * It uses the same keys as GCM downstream messages parameters. + * https://developers.google.com/cloud-messaging/server-ref#notification-payload-support + * + * @author Roman Savin + * @since 23.06.2015 + */ +public final class OPFNotificationPreparer implements NotificationPreparer { + + private static final String TITLE = "title"; + private static final String ICON = "icon"; + private static final String BODY = "body"; + private static final String SOUND = "sound"; + private static final String TAG = "tag"; + private static final String COLOR = "color"; + private static final String CLICK_ACTION = "click_action"; + + @Nullable + @Override + public NotificationPayload prepare(@NonNull final Bundle bundle) { + OPFLog.logMethod(OPFUtils.toString(bundle)); + final String title = bundle.getString(TITLE); + if (TextUtils.isEmpty(title)) { + OPFLog.i("Notification title is empty. Notification will not be shown"); + return null; + } + + final String icon = bundle.getString(ICON); + if (TextUtils.isEmpty(icon)) { + OPFLog.i("Notification icon is empty. Notification will not be shown"); + return null; + } + + final NotificationPayload.Builder builder = new NotificationPayload.Builder() + .setTitle(title) + .setIcon(icon) + .setBody(bundle.getString(BODY)) + .setSound(bundle.getString(SOUND)) + .setTag(bundle.getString(TAG)) + .setColor(bundle.getString(COLOR)) + .setClickAction(bundle.getString(CLICK_ACTION)); + + + return builder.build(); + } +} diff --git a/opfpush/src/main/java/org/onepf/opfpush/pushprovider/PushProvider.java b/opfpush/src/main/java/org/onepf/opfpush/pushprovider/PushProvider.java index 65537275..57c2805f 100644 --- a/opfpush/src/main/java/org/onepf/opfpush/pushprovider/PushProvider.java +++ b/opfpush/src/main/java/org/onepf/opfpush/pushprovider/PushProvider.java @@ -21,6 +21,7 @@ import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; +import org.onepf.opfpush.notification.NotificationMaker; /** * The {@code PushProvider} interface represent the provider for push notification from the server to @@ -92,12 +93,20 @@ public interface PushProvider { @Nullable String getHostAppPackage(); + /** + * Returns the {@link NotificationMaker} object associated with provider. + * + * @return The {@link NotificationMaker} instance. + */ + @NonNull + NotificationMaker getNotificationMaker(); + /** * Verify that application manifest contains all needed permissions. * + * @param checkManifestHandler If not null an exception isn't thrown. * @throws java.lang.IllegalStateException If not all required permissions and components have been * described in the AndroidManifest.xml file. - * @param checkManifestHandler If not null an exception isn't thrown. */ void checkManifest(@Nullable CheckManifestHandler checkManifestHandler); diff --git a/opfpush/src/main/java/org/onepf/opfpush/utils/NotificationUtils.java b/opfpush/src/main/java/org/onepf/opfpush/utils/NotificationUtils.java new file mode 100644 index 00000000..a9d1f086 --- /dev/null +++ b/opfpush/src/main/java/org/onepf/opfpush/utils/NotificationUtils.java @@ -0,0 +1,166 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.opfpush.utils; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import org.onepf.opfpush.model.NotificationPayload; +import org.onepf.opfpush.notification.NotificationPreparer; +import org.onepf.opfpush.notification.OPFNotificationPreparer; +import org.onepf.opfutils.OPFLog; +import org.onepf.opfutils.OPFUtils; + +import java.util.regex.Pattern; + +import static android.app.Notification.DEFAULT_SOUND; +import static android.content.Context.NOTIFICATION_SERVICE; +import static java.util.Locale.US; + +/** + * Utils class intended to create notifications from {@code bundle} using a {@link NotificationPreparer} instance. + * + * @author Roman Savin + * @since 23.06.2015 + */ +public final class NotificationUtils { + + private static final String HEX_COLOR_PATTERN = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"; + private static final Pattern COLOR_PATTERN = Pattern.compile(HEX_COLOR_PATTERN); + + private static final String SOUND_DEFAULT_VALUE = "default"; + + private static final String DEFAULT_TAG_PREFIX = "OPF-Notification:"; + + private NotificationUtils() { + throw new UnsupportedOperationException(); + } + + /** + * Creates a notification from {@code bundle} using the {@link OPFNotificationPreparer}. + * + * @param context The context instance. + * @param bundle Notification data. + */ + public static void showNotification(@NonNull final Context context, + @NonNull final Bundle bundle) { + showNotification(context, bundle, new OPFNotificationPreparer()); + } + + /** + * Creates a notification from {@code bundle}. + * + * @param context The context instance. + * @param bundle Notification data. + * @param notificationPreparer The notification preparer intended to convert bundle to the {@link NotificationPayload} object. + */ + public static void showNotification(@NonNull final Context context, + @NonNull final Bundle bundle, + @NonNull final NotificationPreparer notificationPreparer) { + OPFLog.logMethod(context, OPFUtils.toString(bundle), notificationPreparer); + final NotificationPayload notificationPayload = notificationPreparer.prepare(bundle); + if (notificationPayload == null) { + return; + } + + final String icon = notificationPayload.getIcon(); + final int iconDrawableId = context.getResources().getIdentifier(icon, "drawable", context.getPackageName()); + + if (iconDrawableId == 0) { + OPFLog.i(String.format(US, "Can't find drawable with name : \"%s\". Notification will not be shown", icon)); + return; + } + + final Notification.Builder notificationBuilder = new Notification.Builder(context) + .setContentTitle(notificationPayload.getTitle()) + .setSmallIcon(iconDrawableId) + .setContentText(notificationPayload.getBody()) + .setAutoCancel(true); + + final String sound = notificationPayload.getSound(); + if (!TextUtils.isEmpty(sound)) { + safeSetSound(notificationBuilder, sound); + } + + final String color = notificationPayload.getColor(); + if (!TextUtils.isEmpty(color)) { + safeSetColor(notificationBuilder, color); + } + + final String clickAction = notificationPayload.getClickAction(); + if (!TextUtils.isEmpty(clickAction)) { + setContentIntent(notificationBuilder, context, clickAction, bundle); + } + + final String tag = TextUtils.isEmpty(notificationPayload.getTag()) + ? DEFAULT_TAG_PREFIX + System.currentTimeMillis() + : notificationPayload.getTag(); + final NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + notificationManager.notify(tag, 0, buildNotification(notificationBuilder)); + } + + private static void safeSetSound(@NonNull final Notification.Builder notificationBuilder, + @NonNull final String sound) { + if (SOUND_DEFAULT_VALUE.equals(sound)) { + notificationBuilder.setDefaults(DEFAULT_SOUND); + } else { + OPFLog.i(String.format(US, "Sound \"%s\" is not supported. Only \"default\" sound is supported currently.", sound)); + } + } + + @SuppressLint("NewApi") + private static void safeSetColor(@NonNull final Notification.Builder notificationBuilder, + @NonNull final String color) { + if (!COLOR_PATTERN.matcher(color).matches()) { + OPFLog.i(String.format(US, "Color \"%s\" doesn't match to #rrggbb format.", color)); + } else if (!isSetColorSupported()) { + OPFLog.i("Notification.Builder.setColor() is not supported"); + } else { + notificationBuilder.setColor(Color.parseColor(color)); + } + } + + private static void setContentIntent(@NonNull final Notification.Builder notificationBuilder, + @NonNull final Context context, + @NonNull final String clickAction, + @NonNull final Bundle bundle) { + final Intent intent = new Intent(clickAction); + intent.setPackage(context.getPackageName()); + intent.putExtras(bundle); + + notificationBuilder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)); + } + + private static boolean isSetColorSupported() { + return VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP; + } + + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + private static Notification buildNotification(@NonNull final Notification.Builder builder) { + return VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification(); + } +} diff --git a/opfpush/src/test/java/org/onepf/opfpush/mock/MockPushProvider.java b/opfpush/src/test/java/org/onepf/opfpush/mock/MockPushProvider.java index 29744156..d0aa9afd 100644 --- a/opfpush/src/test/java/org/onepf/opfpush/mock/MockPushProvider.java +++ b/opfpush/src/test/java/org/onepf/opfpush/mock/MockPushProvider.java @@ -7,6 +7,8 @@ import org.onepf.opfpush.listener.CheckManifestHandler; import org.onepf.opfpush.model.AvailabilityResult; import org.onepf.opfpush.model.PushError; +import org.onepf.opfpush.notification.NotificationMaker; +import org.onepf.opfpush.notification.OPFNotificationMaker; import org.onepf.opfpush.pushprovider.PushProvider; import org.onepf.opfpush.testutil.Util; @@ -101,6 +103,12 @@ public String getHostAppPackage() { return hostAppPackage; } + @NonNull + @Override + public NotificationMaker getNotificationMaker() { + return new OPFNotificationMaker(); + } + @Override public void checkManifest(@Nullable CheckManifestHandler checkManifestHandler) { // nothing diff --git a/samples/pushchat/build.gradle b/samples/pushchat/build.gradle index 90af0d59..bacd201d 100644 --- a/samples/pushchat/build.gradle +++ b/samples/pushchat/build.gradle @@ -23,8 +23,8 @@ android { applicationId "org.onepf.opfpush.pushsample" minSdkVersion 15 targetSdkVersion 22 - versionCode 1 - versionName "0.2.2" + versionCode 2 + versionName "0.3.0" } buildTypes { @@ -47,21 +47,19 @@ android { dependencies { //material - //noinspection ObsoleteGradleDependency - compile 'com.android.support:appcompat-v7:22.1.1' + compile 'com.android.support:appcompat-v7:22.2.0' compile 'com.melnykov:floatingactionbutton:1.3.0' //opfpush compile 'org.onepf:opfutils:0.1.22' - compile 'org.onepf:opfpush:0.2.3@aar' - compile 'org.onepf:opfpush-adm:0.2.3@aar' - compile 'org.onepf:opfpush-gcm:0.2.3@aar' - compile 'org.onepf:opfpush-nokia:0.2.3@aar' + compile 'org.onepf:opfpush:0.3.0@aar' + compile 'org.onepf:opfpush-gcm:0.3.0@aar' + compile 'org.onepf:opfpush-adm:0.3.0@aar' + compile 'org.onepf:opfpush-nokia:0.3.0@aar' //push providers dependencies provided 'com.amazon:amazon-device-messaging:1.0.1' - //noinspection ObsoleteGradleDependency - compile 'com.google.android.gms:play-services:7.3.0' + compile 'com.google.android.gms:play-services:7.5.0' compile 'com.nokia:push:1.0' //network diff --git a/samples/pushchat/proguard-project.txt b/samples/pushchat/proguard-project.txt index 8759330e..75ecca96 100644 --- a/samples/pushchat/proguard-project.txt +++ b/samples/pushchat/proguard-project.txt @@ -30,6 +30,11 @@ #appcompat -dontwarn android.view.accessibility.AccessibilityNodeInfo -dontwarn android.media.session.MediaSession +-dontwarn android.support.v4.app.DialogFragment +-dontwarn android.support.v4.app.FragmentTransaction +-dontwarn android.support.v4.view.ViewCompat +-dontwarn android.support.v4.widget.DrawerLayout +-dontwarn android.support.v7.media.** #retrofit -keepattributes Signature diff --git a/samples/pushchat/src/main/AndroidManifest.xml b/samples/pushchat/src/main/AndroidManifest.xml index e1c8dac7..a9b84e45 100644 --- a/samples/pushchat/src/main/AndroidManifest.xml +++ b/samples/pushchat/src/main/AndroidManifest.xml @@ -37,6 +37,7 @@ + @@ -56,17 +57,14 @@ + - - - - diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/PushChatApplication.java b/samples/pushchat/src/main/java/org/onepf/pushchat/PushChatApplication.java index fd3de800..b4153185 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/PushChatApplication.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/PushChatApplication.java @@ -17,7 +17,6 @@ package org.onepf.pushchat; import android.app.Application; - import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; import org.onepf.opfpush.OPFPush; diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/controller/StateController.java b/samples/pushchat/src/main/java/org/onepf/pushchat/controller/StateController.java index 97b7deb4..996d5541 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/controller/StateController.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/controller/StateController.java @@ -84,7 +84,7 @@ public static void putNoAvailableProviderValue(@NonNull final Context context, .apply(); } - private static boolean isRegIdSavedOnServer(@NonNull final Context context) { + public static boolean isRegIdSavedOnServer(@NonNull final Context context) { final SharedPreferences sharedPreferences = context .getSharedPreferences(STATE_PREFS_NAME, MODE_PRIVATE); return sharedPreferences.getBoolean(IS_REGID_SAVED_KEY, false); diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/model/request/SubscribeOrUnsubscribeRequestBody.java b/samples/pushchat/src/main/java/org/onepf/pushchat/model/request/SubscribeOrUnsubscribeRequestBody.java new file mode 100644 index 00000000..9dc073a2 --- /dev/null +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/model/request/SubscribeOrUnsubscribeRequestBody.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.pushchat.model.request; + +import android.support.annotation.NonNull; + +/** + * @author Roman Savin + * @since 03.07.2015 + */ +//CHECKSTYLE:OFF +public final class SubscribeOrUnsubscribeRequestBody { + + @NonNull + public final String topic; + + @NonNull + public final String uuid; + + public SubscribeOrUnsubscribeRequestBody(@NonNull final String topic, @NonNull final String uuid) { + this.topic = topic; + this.uuid = uuid; + } +} diff --git a/opfpush-providers/gcm/src/main/java/org/onepf/opfpush/gcm/GCMAction.java b/samples/pushchat/src/main/java/org/onepf/pushchat/model/response/TopicsResponse.java similarity index 58% rename from opfpush-providers/gcm/src/main/java/org/onepf/opfpush/gcm/GCMAction.java rename to samples/pushchat/src/main/java/org/onepf/pushchat/model/response/TopicsResponse.java index 60fabdc1..c92dcaff 100644 --- a/opfpush-providers/gcm/src/main/java/org/onepf/opfpush/gcm/GCMAction.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/model/response/TopicsResponse.java @@ -14,21 +14,25 @@ * limitations under the License. */ -package org.onepf.opfpush.gcm; +package org.onepf.pushchat.model.response; -import android.support.annotation.StringDef; +import android.support.annotation.Nullable; +import com.google.gson.annotations.SerializedName; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.List; /** - * @author Kirill Rozov - * @since 24.09.14. + * @author Roman Savin + * @since 02.07.2015 */ -@Retention(RetentionPolicy.SOURCE) -@StringDef({ - GCMConstants.ACTION_REGISTRATION_CALLBACK, - GCMConstants.ACTION_UNREGISTRATION_CALLBACK -}) -@interface GCMAction { +//CHECKSTYLE:OFF +public final class TopicsResponse { + + @SerializedName("topics") + @Nullable + public final List topics; + + public TopicsResponse(@Nullable final List topics) { + this.topics = topics; + } } diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/NetworkController.java b/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/NetworkController.java index e27e74d5..6ec6ecab 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/NetworkController.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/NetworkController.java @@ -26,10 +26,12 @@ import org.onepf.pushchat.db.DatabaseHelper; import org.onepf.pushchat.db.DatabaseHelper.ContactsUuidsAsyncQueryHandler.QueryContactsUuidsCallback; import org.onepf.pushchat.model.request.RegistrationRequestBody; +import org.onepf.pushchat.model.request.SubscribeOrUnsubscribeRequestBody; import org.onepf.pushchat.model.request.UnregistrationRequestBody; import org.onepf.pushchat.model.request.push.PushMessageRequestBody; import org.onepf.pushchat.model.response.ExistResponse; import org.onepf.pushchat.model.response.RegistrationResponse; +import org.onepf.pushchat.model.response.TopicsResponse; import org.onepf.pushchat.model.response.UnregistrationResponse; import org.onepf.pushchat.model.response.push.PushMessageResponse; import org.onepf.pushchat.utils.Constants; @@ -108,6 +110,23 @@ public void exist(@NonNull final String uuid, pushService.exist(uuid, callback); } + public void getTopics(@NonNull final Context context, + @NonNull final Callback callback) { + pushService.getTopics(getUuid(context), callback); + } + + public void subscribe(@NonNull final Context context, + @NonNull final String topic, + @NonNull final Callback callback) { + pushService.subscribe(new SubscribeOrUnsubscribeRequestBody(topic, getUuid(context)), callback); + } + + public void unsubscribe(@NonNull final Context context, + @NonNull final String topic, + @NonNull final Callback callback) { + pushService.unsubscribe(new SubscribeOrUnsubscribeRequestBody(topic, getUuid(context)), callback); + } + private String getUuid(@NonNull final Context context) { final PushChatApplication application = (PushChatApplication) context.getApplicationContext(); return application.getUUID(); diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/PushService.java b/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/PushService.java index e16ac72b..642e0625 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/PushService.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/retrofit/PushService.java @@ -19,10 +19,12 @@ import android.support.annotation.NonNull; import org.onepf.pushchat.model.request.RegistrationRequestBody; +import org.onepf.pushchat.model.request.SubscribeOrUnsubscribeRequestBody; import org.onepf.pushchat.model.request.UnregistrationRequestBody; import org.onepf.pushchat.model.request.push.PushMessageRequestBody; import org.onepf.pushchat.model.response.ExistResponse; import org.onepf.pushchat.model.response.RegistrationResponse; +import org.onepf.pushchat.model.response.TopicsResponse; import org.onepf.pushchat.model.response.UnregistrationResponse; import org.onepf.pushchat.model.response.push.PushMessageResponse; @@ -53,4 +55,16 @@ void push(@Body @NonNull final PushMessageRequestBody body, @GET("/exist") void exist(@Query("uuid") @NonNull final String uuid, final Callback callback); + + @GET("/getTopicsByUuid") + void getTopics(@Query("uuid") @NonNull final String uuid, + @NonNull final Callback callback); + + @POST("/subscribe") + void subscribe(@Body @NonNull final SubscribeOrUnsubscribeRequestBody body, + @NonNull final Callback callback); + + @POST("/unsubscribe") + void unsubscribe(@Body @NonNull final SubscribeOrUnsubscribeRequestBody body, + @NonNull final Callback callback); } diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/ContentFragmentFactory.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/ContentFragmentFactory.java index 33dbfde8..5176d953 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/ContentFragmentFactory.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/ContentFragmentFactory.java @@ -22,6 +22,7 @@ import org.onepf.pushchat.ui.fragment.content.ContactsFragment; import org.onepf.pushchat.ui.fragment.content.MessagesFragment; import org.onepf.pushchat.ui.fragment.content.StateFragment; +import org.onepf.pushchat.ui.fragment.content.TopicsFragment; /** * @author Roman Savin @@ -38,6 +39,8 @@ public static BaseContentFragment getFragmentByPosition(final int position) { switch (position) { case StateFragment.POSITION: return StateFragment.newInstance(); + case TopicsFragment.POSITION: + return TopicsFragment.newInstance(); case MessagesFragment.POSITION: return MessagesFragment.newInstance(); case ContactsFragment.POSITION: diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/activity/MainActivity.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/activity/MainActivity.java index 0943c123..44b1dabc 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/activity/MainActivity.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/activity/MainActivity.java @@ -26,18 +26,20 @@ import android.support.annotation.Nullable; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.widget.ProgressBar; import com.google.android.gms.common.GooglePlayServicesUtil; +import org.onepf.opfpush.OPFPush; +import org.onepf.opfpush.OPFPushHelper; import org.onepf.pushchat.PushChatApplication; import org.onepf.pushchat.R; import org.onepf.pushchat.controller.StateController; import org.onepf.pushchat.db.DatabaseHelper; +import org.onepf.pushchat.retrofit.NetworkController; import org.onepf.pushchat.ui.fragment.NavigationDrawerFragment; import org.onepf.pushchat.ui.fragment.content.BaseContentFragment; import org.onepf.pushchat.ui.fragment.content.MessagesFragment; @@ -52,8 +54,8 @@ import static org.onepf.pushchat.ui.activity.MainActivity.MainActivityReceiver.SHOW_GCM_ERROR_DIALOG_ACTION; import static org.onepf.pushchat.ui.activity.MainActivity.MainActivityReceiver.SHOW_PROGRESS_BAR_ACTION; - -public class MainActivity extends ActionBarActivity { +@SuppressWarnings("PMD.GodClass") +public class MainActivity extends AppCompatActivity { public static final String OPEN_MESSAGES_FRAGMENT_ACTION = "OPEN_MESSAGES_FRAGMENT_ACTION"; @@ -76,6 +78,7 @@ public class MainActivity extends ActionBarActivity { private boolean isClearMenuItemVisible; + @Nullable private MainActivityReceiver receiver; @Override @@ -83,6 +86,14 @@ protected void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + final OPFPushHelper helper = OPFPush.getHelper(); + if (helper.isRegistered() + && helper.getProviderName() != null + && helper.getRegistrationId() != null + && !StateController.isRegIdSavedOnServer(this)) { + NetworkController.getInstance().register(this, helper.getProviderName(), helper.getRegistrationId()); + } + progressBar = (ProgressBar) findViewById(R.id.progress_bar); setupNavigationDrawer(); if (savedInstanceState == null) { @@ -185,8 +196,8 @@ public void onConfigurationChanged(final Configuration newConfig) { @Override public void onBackPressed() { - if (drawerLayout.isDrawerOpen(Gravity.START)) { - drawerLayout.closeDrawer(Gravity.START); + if (drawerLayout.isDrawerOpen(GravityCompat.START)) { + drawerLayout.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } @@ -269,12 +280,6 @@ private void setupNavigationDrawer() { drawerLayout.setDrawerListener(drawerToggle); } - private void showGcmErrorDialog(final int errorCode) { - if (errorCode != -1) { - GooglePlayServicesUtil.showErrorDialogFragment(errorCode, this, 0); - } - } - private void onShareClick() { final Intent intent = new Intent(ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, @@ -310,5 +315,11 @@ public void onReceive(@NonNull final Context context, @NonNull final Intent inte break; } } + + private void showGcmErrorDialog(final int errorCode) { + if (errorCode != -1) { + GooglePlayServicesUtil.showErrorDialogFragment(errorCode, MainActivity.this, 0); + } + } } } diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/dialog/UnsubscribeDialogFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/dialog/UnsubscribeDialogFragment.java new file mode 100644 index 00000000..c455d499 --- /dev/null +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/dialog/UnsubscribeDialogFragment.java @@ -0,0 +1,155 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.pushchat.ui.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.widget.Toast; +import org.onepf.opfpush.gcm.GCMPubSubHelper; +import org.onepf.opfutils.OPFLog; +import org.onepf.pushchat.R; +import org.onepf.pushchat.retrofit.NetworkController; +import retrofit.Callback; +import retrofit.RetrofitError; +import retrofit.client.Response; + +import static android.widget.Toast.LENGTH_SHORT; + +/** + * @author Roman Savin + * @since 08.07.2015 + */ +public class UnsubscribeDialogFragment extends DialogFragment { + + public static final String TAG = UnsubscribeDialogFragment.class.getName(); + + private static final String TOPIC_EXTRA_KEY = "TOPIC_EXTRA_KEY"; + + @Nullable + private OnUnsubscribeCallback onUnsubscribeCallback; + + public static UnsubscribeDialogFragment newInstance(@NonNull final String topic) { + final UnsubscribeDialogFragment dialogFragment = new UnsubscribeDialogFragment(); + final Bundle arguments = new Bundle(); + arguments.putString(TOPIC_EXTRA_KEY, topic); + dialogFragment.setArguments(arguments); + return dialogFragment; + } + + @NonNull + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + final String topic = getArguments().getString(TOPIC_EXTRA_KEY); + final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); + + dialogBuilder.setMessage(getString(R.string.unsubscribe_question_fmt, topic)) + .setPositiveButton(android.R.string.ok, new OnUnsubscribeClickListener(topic)) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + + return dialogBuilder.create(); + } + + public void setOnUnsubscribeCallback(@NonNull final OnUnsubscribeCallback onUnsubscribeCallback) { + this.onUnsubscribeCallback = onUnsubscribeCallback; + } + + public interface OnUnsubscribeCallback { + + void onUnsubscribe(); + } + + private final class OnUnsubscribeClickListener implements DialogInterface.OnClickListener { + + @NonNull + private final String topic; + + public OnUnsubscribeClickListener(@NonNull final String topic) { + this.topic = topic; + } + + @Override + public void onClick(final DialogInterface dialog, final int which) { + final Context context = getActivity(); + GCMPubSubHelper.getInstance(context).unsubscribe(topic, new OnGcmUnsubscribeCallback(topic)); + NetworkController.getInstance().unsubscribe(context, topic, new OnServerUnsubscribeCallback(topic)); + } + } + + private final class OnGcmUnsubscribeCallback implements GCMPubSubHelper.Callback { + + @NonNull + private final String topic; + + public OnGcmUnsubscribeCallback(@NonNull final String topic) { + this.topic = topic; + } + + @Override + public void onSuccess() { + OPFLog.logMethod(); + } + + @Override + public void onError(@Nullable final String error) { + OPFLog.logMethod(error); + final Context context = getActivity(); + if (context != null) { + Toast.makeText(context, error, LENGTH_SHORT).show(); + } + } + } + + private final class OnServerUnsubscribeCallback implements Callback { + + @NonNull + private final String topic; + + public OnServerUnsubscribeCallback(@NonNull final String topic) { + this.topic = topic; + } + + @Override + public void success(final Object o, final Response response) { + OPFLog.logMethod(o, response); + + if (onUnsubscribeCallback != null) { + onUnsubscribeCallback.onUnsubscribe(); + } + } + + @Override + public void failure(final RetrofitError error) { + OPFLog.logMethod(error); + + final Context context = getActivity(); + if (context != null) { + Toast.makeText(context, error.getMessage(), LENGTH_SHORT).show(); + } + } + } +} diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/NavigationDrawerFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/NavigationDrawerFragment.java index 8de5e862..79909f08 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/NavigationDrawerFragment.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/NavigationDrawerFragment.java @@ -58,6 +58,7 @@ public void onCreate(@Nullable final Bundle savedInstanceState) { titles = new String[]{ getString(R.string.title_state_fragment), + getString(R.string.title_topics_fragment), getString(R.string.title_messages_fragment), getString(R.string.title_contacts_fragment), getString(R.string.title_about_fragment) @@ -90,6 +91,7 @@ public View onCreateView(@NonNull final LayoutInflater inflater, return drawerListView; } + @SuppressWarnings("AssignmentToNull") @Override public void onDestroyView() { super.onDestroyView(); diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/AboutFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/AboutFragment.java index 0867fea1..4d606485 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/AboutFragment.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/AboutFragment.java @@ -34,7 +34,7 @@ */ public class AboutFragment extends BaseContentFragment { - public static final int POSITION = 3; + public static final int POSITION = 4; @NonNull public static AboutFragment newInstance() { diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/ContactsFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/ContactsFragment.java index f1346dd3..0ef0e391 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/ContactsFragment.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/ContactsFragment.java @@ -43,7 +43,7 @@ */ public class ContactsFragment extends BaseContentFragment { - public static final int POSITION = 2; + public static final int POSITION = 3; private ContactsCursorAdapter adapter; private ListView contactsListView; @@ -67,6 +67,7 @@ public View onCreateView(@NonNull final LayoutInflater inflater, return view; } + @SuppressWarnings("AssignmentToNull") @Override public void onDestroyView() { super.onDestroyView(); diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/MessagesFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/MessagesFragment.java index 93e2d282..b8a58a59 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/MessagesFragment.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/MessagesFragment.java @@ -66,7 +66,7 @@ */ public class MessagesFragment extends BaseContentFragment { - public static final int POSITION = 1; + public static final int POSITION = 2; private MessagesCursorAdapter adapter; @@ -74,6 +74,7 @@ public class MessagesFragment extends BaseContentFragment { private Button sendButton; + @Nullable private UpdateStateReceiver receiver; private MessagesLoaderCallbacks loaderCallbacks; @@ -112,6 +113,7 @@ public void onPause() { hideKeyboard(getView()); } + @SuppressWarnings("AssignmentToNull") @Override public void onDestroyView() { super.onDestroyView(); @@ -130,7 +132,7 @@ public int getTitleResId() { @Override public int getPosition() { - return 1; + return POSITION; } private void initMessagesEditText(@NonNull final View view) { @@ -229,24 +231,6 @@ private void unregisterReceiver() { } } - private void enableMessageEditText() { - if (messageEditText != null) { - messageEditText.setEnabled(true); - } - if (sendButton != null) { - sendButton.setEnabled(true); - } - } - - private void disableMessageEditText() { - if (messageEditText != null) { - messageEditText.setEnabled(false); - } - if (sendButton != null) { - sendButton.setEnabled(false); - } - } - private Callback pushMessageCallback(@NonNull final String messageText) { return new Callback() { @@ -305,6 +289,24 @@ public void onReceive(Context context, Intent intent) { break; } } + + private void enableMessageEditText() { + if (messageEditText != null) { + messageEditText.setEnabled(true); + } + if (sendButton != null) { + sendButton.setEnabled(true); + } + } + + private void disableMessageEditText() { + if (messageEditText != null) { + messageEditText.setEnabled(false); + } + if (sendButton != null) { + sendButton.setEnabled(false); + } + } } private class MessagesLoaderCallbacks implements LoaderManager.LoaderCallbacks { diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/StateFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/StateFragment.java index 0ca7bfde..a42214c4 100644 --- a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/StateFragment.java +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/StateFragment.java @@ -56,6 +56,7 @@ public class StateFragment extends BaseContentFragment { private Button registerButton; + @Nullable private BroadcastReceiver updateStateReceiver; @NonNull @@ -77,7 +78,7 @@ public View onCreateView(@NonNull final LayoutInflater inflater, registrationIdTextView = (TextView) view.findViewById(R.id.registration_id_text); registerButton = (Button) view.findViewById(R.id.register_button); - registerButton.setOnClickListener(onClickListener()); + registerButton.setOnClickListener(onRegistrationClickListener()); registerReceiver(); initState(); @@ -85,6 +86,7 @@ public View onCreateView(@NonNull final LayoutInflater inflater, return view; } + @SuppressWarnings("AssignmentToNull") @Override public void onDestroyView() { super.onDestroyView(); @@ -215,7 +217,7 @@ public void onReceive(@NonNull final Context context, @NonNull final Intent inte } } - private View.OnClickListener onClickListener() { + private View.OnClickListener onRegistrationClickListener() { return new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/TopicsFragment.java b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/TopicsFragment.java new file mode 100644 index 00000000..a03d243f --- /dev/null +++ b/samples/pushchat/src/main/java/org/onepf/pushchat/ui/fragment/content/TopicsFragment.java @@ -0,0 +1,358 @@ +/* + * Copyright 2012-2015 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.pushchat.ui.fragment.content; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; +import org.onepf.opfpush.OPFPush; +import org.onepf.opfpush.gcm.GCMConstants; +import org.onepf.opfpush.gcm.GCMPubSubHelper; +import org.onepf.opfutils.OPFLog; +import org.onepf.pushchat.R; +import org.onepf.pushchat.controller.StateController; +import org.onepf.pushchat.model.response.TopicsResponse; +import org.onepf.pushchat.retrofit.NetworkController; +import org.onepf.pushchat.ui.dialog.UnsubscribeDialogFragment; +import retrofit.Callback; +import retrofit.RetrofitError; +import retrofit.client.Response; + +import java.util.List; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static android.widget.Toast.LENGTH_SHORT; +import static org.onepf.pushchat.model.PushState.REGISTERED; +import static org.onepf.pushchat.utils.Constants.REGISTERED_ACTION; +import static org.onepf.pushchat.utils.Constants.UNREGISTERED_ACTION; + +/** + * @author Roman Savin + * @since 02.07.2015 + */ +@SuppressWarnings("PMD.TooManyMethods") +public class TopicsFragment extends BaseContentFragment { + + public static final int POSITION = 1; + + private TextView infoText; + + private EditText topicEditText; + + private Button subscribeButton; + + private ListView topicsListView; + + private ProgressBar progressBar; + + private ArrayAdapter adapter; + + @Nullable + private BroadcastReceiver receiver; + + public static TopicsFragment newInstance() { + return new TopicsFragment(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + adapter = new ArrayAdapter<>(getActivity(), R.layout.item_topic); + } + + @Nullable + @Override + public View onCreateView(@NonNull final LayoutInflater inflater, + @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.fragment_topics, container, false); + + infoText = (TextView) view.findViewById(R.id.topic_info); + topicEditText = (EditText) view.findViewById(R.id.topic_input); + subscribeButton = (Button) view.findViewById(R.id.subscribe_topic_button); + topicsListView = (ListView) view.findViewById(R.id.topics_list); + progressBar = (ProgressBar) view.findViewById(R.id.topic_progress_bar); + + subscribeButton.setOnClickListener(new OnSubscribeClickListener()); + topicEditText.addTextChangedListener(new TopicTextWatcher()); + topicsListView.setAdapter(adapter); + topicsListView.setOnItemLongClickListener(new OnTopicItemLongLickListener()); + + registerReceiver(); + initViews(); + + return view; + } + + @SuppressWarnings("AssignmentToNull") + @Override + public void onDestroyView() { + super.onDestroyView(); + unregisterReceiver(); + infoText = null; + topicEditText = null; + subscribeButton = null; + topicsListView = null; + } + + @Override + public void onPause() { + super.onPause(); + hideKeyboard(getView()); + } + + @SuppressWarnings("AssignmentToNull") + @Override + public void onDetach() { + super.onDetach(); + adapter = null; + } + + @Override + public int getTitleResId() { + return R.string.title_topics_fragment; + } + + @Override + public int getPosition() { + return POSITION; + } + + private void initViews() { + final String currentProviderName = OPFPush.getHelper().getProviderName(); + + if (TextUtils.isEmpty(currentProviderName)) { + initTopicsUnavailableVisibility(getString(R.string.no_available_provider)); + } else if (!GCMConstants.PROVIDER_NAME.equals(currentProviderName)) { + initTopicsUnavailableVisibility(getString(R.string.provider_not_support_topics, currentProviderName)); + } else { + initTopicsAvailableVisibility(); + loadTopics(); + } + } + + private void initTopicsUnavailableVisibility(@NonNull final String reason) { + infoText.setVisibility(VISIBLE); + infoText.setText(reason); + + topicEditText.setVisibility(GONE); + subscribeButton.setVisibility(GONE); + topicsListView.setVisibility(GONE); + } + + private void initTopicsAvailableVisibility() { + infoText.setVisibility(GONE); + topicEditText.setVisibility(VISIBLE); + subscribeButton.setVisibility(VISIBLE); + topicsListView.setVisibility(VISIBLE); + } + + private void loadTopics() { + safeSetProgressBarVisibility(VISIBLE); + NetworkController.getInstance().getTopics(getActivity(), new LoadTopicsCallback()); + } + + private void safeSetProgressBarVisibility(final int visibility) { + if (progressBar != null) { + progressBar.setVisibility(visibility); + } + } + + private void registerReceiver() { + if (receiver == null) { + receiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + initViews(); + } + }; + + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(REGISTERED_ACTION); + intentFilter.addAction(UNREGISTERED_ACTION); + getActivity().registerReceiver(receiver, intentFilter); + } + } + + private void unregisterReceiver() { + if (receiver != null) { + getActivity().unregisterReceiver(receiver); + receiver = null; + } + } + + private final class TopicTextWatcher implements TextWatcher { + @Override + public void afterTextChanged(final Editable s) { + final boolean isEnabled = !s.toString().isEmpty() && StateController.getState(getActivity()) == REGISTERED; + subscribeButton.setEnabled(isEnabled); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + //nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + //nothing + } + } + + private final class OnTopicItemLongLickListener implements AdapterView.OnItemLongClickListener { + + @Override + public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { + if (adapter == null) { + return false; + } + final String topic = adapter.getItem(position); + final UnsubscribeDialogFragment unsubscribeDialogFragment = UnsubscribeDialogFragment.newInstance(topic); + unsubscribeDialogFragment.setOnUnsubscribeCallback(new UnsubscribeDialogFragment.OnUnsubscribeCallback() { + @Override + public void onUnsubscribe() { + adapter.remove(topic); + } + }); + unsubscribeDialogFragment.show(getFragmentManager(), UnsubscribeDialogFragment.TAG); + return true; + } + } + + private final class OnSubscribeClickListener implements View.OnClickListener { + + @Override + public void onClick(@NonNull final View v) { + final Context context = getActivity(); + if (topicEditText == null || context == null) { + return; + } + + final String topic = context.getString(R.string.topic_fmt, topicEditText.getText().toString()); + topicEditText.setText(""); + GCMPubSubHelper.getInstance(context).subscribe(topic, null, new OnGcmSubscribeCallback(topic)); + } + } + + private final class OnGcmSubscribeCallback implements GCMPubSubHelper.Callback { + + @NonNull + private final String topic; + + public OnGcmSubscribeCallback(@NonNull final String topic) { + this.topic = topic; + } + + @Override + public void onSuccess() { + OPFLog.logMethod(); + final Context context = getActivity(); + if (context == null) { + return; + } + + NetworkController.getInstance().subscribe(context, topic, new OnServerSubscribeCallback(topic)); + } + + @Override + public void onError(@Nullable final String error) { + OPFLog.logMethod(error); + final Context context = getActivity(); + if (context != null) { + Toast.makeText(context, error, LENGTH_SHORT).show(); + } + } + } + + private final class OnServerSubscribeCallback implements Callback { + + @NonNull + private final String topic; + + public OnServerSubscribeCallback(@NonNull final String topic) { + this.topic = topic; + } + + @Override + public void success(final Object o, final Response response) { + OPFLog.logMethod(o, response); + //Add topic to adapter if it's not already added. + if (adapter != null && topicsListView != null && adapter.getPosition(topic) < 0) { + adapter.add(topic); + } + } + + @Override + public void failure(final RetrofitError error) { + OPFLog.logMethod(error); + final Context context = getActivity(); + if (context != null) { + Toast.makeText(context, error.getMessage(), LENGTH_SHORT).show(); + } + } + } + + private final class LoadTopicsCallback implements Callback { + + @Override + public void success(@NonNull final TopicsResponse topicsResponse, @NonNull final Response response) { + OPFLog.logMethod(topicsResponse, response); + safeSetProgressBarVisibility(GONE); + initTopicsList(topicsResponse.topics); + } + + @Override + public void failure(@NonNull final RetrofitError error) { + OPFLog.logMethod(error); + OPFLog.e(error.getMessage()); + safeSetProgressBarVisibility(GONE); + final Context context = getActivity(); + if (context != null) { + Toast.makeText(context, error.getMessage(), LENGTH_SHORT).show(); + } + } + + private void initTopicsList(@Nullable final List topics) { + if (topicsListView == null || adapter == null || topics == null) { + return; + } + + adapter.clear(); + adapter.addAll(topics); + } + } +} diff --git a/samples/pushchat/src/main/res/layout/fragment_topics.xml b/samples/pushchat/src/main/res/layout/fragment_topics.xml new file mode 100644 index 00000000..cbcc04cf --- /dev/null +++ b/samples/pushchat/src/main/res/layout/fragment_topics.xml @@ -0,0 +1,57 @@ + + + + + + + + +