From 4a794e46ad54d8255b349cafa0051494e4c16770 Mon Sep 17 00:00:00 2001 From: Michael Russo Date: Fri, 2 Sep 2016 10:27:51 -0700 Subject: [PATCH] Initial commit of Usergrid Android SDK in its own repo. --- .gitignore | 3 + LICENSE | 19 + README.md | 590 ++++++++++++++++++ Samples/ActivityFeed/.gitignore | 8 + Samples/ActivityFeed/activityfeed/.gitignore | 1 + .../ActivityFeed/activityfeed/build.gradle | 27 + .../ActivityFeed/activityfeed/libs/gcm.jar | Bin 0 -> 13662 bytes .../activityfeed/proguard-rules.pro | 17 + .../activityfeed/ApplicationTest.java | 13 + .../activityfeed/src/main/AndroidManifest.xml | 53 ++ .../usergrid/activityfeed/ActivityEntity.java | 86 +++ .../activityfeed/GCMIntentService.java | 87 +++ .../activityfeed/UsergridManager.java | 259 ++++++++ .../activities/CreateAccountActivity.java | 67 ++ .../activityfeed/activities/FeedActivity.java | 142 +++++ .../activities/FollowActivity.java | 59 ++ .../activityfeed/activities/MainActivity.java | 87 +++ .../callbacks/GetFeedMessagesCallback.java | 27 + .../callbacks/PostFeedMessageCallback.java | 25 + .../helpers/ActionBarHelpers.java | 53 ++ .../helpers/AlertDialogHelpers.java | 61 ++ .../activityfeed/helpers/FeedAdapter.java | 153 +++++ .../src/main/res/drawable/in_message_bg.9.png | Bin 0 -> 1160 bytes .../main/res/drawable/out_message_bg.9.png | Bin 0 -> 1072 bytes .../src/main/res/drawable/usergridguy.png | Bin 0 -> 6230 bytes .../src/main/res/layout/action_bar_layout.xml | 29 + .../res/layout/activity_create_account.xml | 101 +++ .../src/main/res/layout/activity_feed.xml | 46 ++ .../src/main/res/layout/activity_follow.xml | 44 ++ .../src/main/res/layout/activity_main.xml | 85 +++ .../src/main/res/layout/message_layout.xml | 43 ++ .../main/res/layout/scrollable_alert_view.xml | 35 ++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes .../src/main/res/values-w820dp/dimens.xml | 6 + .../src/main/res/values/colors.xml | 8 + .../src/main/res/values/dimens.xml | 5 + .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/styles.xml | 19 + .../activityfeed/ExampleUnitTest.java | 15 + Samples/ActivityFeed/build.gradle | 23 + Samples/ActivityFeed/gradle.properties | 18 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + Samples/ActivityFeed/gradlew | 160 +++++ Samples/ActivityFeed/gradlew.bat | 90 +++ Samples/ActivityFeed/settings.gradle | 2 + Samples/Push/.gitignore | 8 + Samples/Push/build.gradle | 22 + Samples/Push/gradle.properties | 18 + .../Push/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + Samples/Push/gradlew | 160 +++++ Samples/Push/gradlew.bat | 90 +++ Samples/Push/push/.gitignore | 1 + Samples/Push/push/build.gradle | 27 + Samples/Push/push/libs/gcm.jar | Bin 0 -> 13662 bytes Samples/Push/push/proguard-rules.pro | 17 + .../apache/usergrid/push/ApplicationTest.java | 13 + .../Push/push/src/main/AndroidManifest.xml | 51 ++ .../usergrid/push/GCMIntentService.java | 85 +++ .../apache/usergrid/push/MainActivity.java | 162 +++++ .../usergrid/push/SettingsActivity.java | 68 ++ .../Push/push/src/main/res/drawable/info.png | Bin 0 -> 44546 bytes .../src/main/res/drawable/usergridguy.png | Bin 0 -> 6230 bytes .../src/main/res/layout/activity_main.xml | 69 ++ .../src/main/res/layout/activity_settings.xml | 95 +++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes .../src/main/res/values-w820dp/dimens.xml | 6 + .../Push/push/src/main/res/values/colors.xml | 6 + .../Push/push/src/main/res/values/dimens.xml | 5 + .../Push/push/src/main/res/values/strings.xml | 3 + .../Push/push/src/main/res/values/styles.xml | 11 + .../apache/usergrid/push/ExampleUnitTest.java | 15 + Samples/Push/settings.gradle | 2 + UsergridAndroidSDK/.gitignore | 8 + UsergridAndroidSDK/build.gradle | 70 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + UsergridAndroidSDK/gradlew | 160 +++++ UsergridAndroidSDK/gradlew.bat | 90 +++ .../libs/usergrid-java-client-2.1.0.jar | Bin 0 -> 1991936 bytes UsergridAndroidSDK/proguard-rules.pro | 17 + .../usergrid/android/ApplicationTest.java | 75 +++ .../org/apache/usergrid/android/Book.java | 26 + .../src/main/AndroidManifest.xml | 17 + .../usergrid/android/UsergridAsync.java | 474 ++++++++++++++ .../usergrid/android/UsergridEntityAsync.java | 110 ++++ .../android/UsergridResponseAsync.java | 38 ++ .../android/UsergridSharedDevice.java | 175 ++++++ .../usergrid/android/UsergridUserAsync.java | 125 ++++ .../UsergridCheckAvailabilityCallback.java | 21 + .../callbacks/UsergridResponseCallback.java | 24 + .../android/tasks/UsergridAsyncTask.java | 45 ++ .../src/main/res/values/strings.xml | 3 + 102 files changed, 4999 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Samples/ActivityFeed/.gitignore create mode 100644 Samples/ActivityFeed/activityfeed/.gitignore create mode 100644 Samples/ActivityFeed/activityfeed/build.gradle create mode 100755 Samples/ActivityFeed/activityfeed/libs/gcm.jar create mode 100644 Samples/ActivityFeed/activityfeed/proguard-rules.pro create mode 100644 Samples/ActivityFeed/activityfeed/src/androidTest/java/org/apache/usergrid/activityfeed/ApplicationTest.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/AndroidManifest.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/ActivityEntity.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/GCMIntentService.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/UsergridManager.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/CreateAccountActivity.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FeedActivity.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FollowActivity.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/MainActivity.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/GetFeedMessagesCallback.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/PostFeedMessageCallback.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/ActionBarHelpers.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/AlertDialogHelpers.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/FeedAdapter.java create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/drawable/in_message_bg.9.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/drawable/out_message_bg.9.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/drawable/usergridguy.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/action_bar_layout.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_create_account.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_feed.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_follow.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_main.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/message_layout.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/layout/scrollable_alert_view.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/values-w820dp/dimens.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/values/colors.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/values/dimens.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/values/strings.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/main/res/values/styles.xml create mode 100644 Samples/ActivityFeed/activityfeed/src/test/java/org/apache/usergrid/activityfeed/ExampleUnitTest.java create mode 100644 Samples/ActivityFeed/build.gradle create mode 100644 Samples/ActivityFeed/gradle.properties create mode 100644 Samples/ActivityFeed/gradle/wrapper/gradle-wrapper.jar create mode 100644 Samples/ActivityFeed/gradle/wrapper/gradle-wrapper.properties create mode 100755 Samples/ActivityFeed/gradlew create mode 100644 Samples/ActivityFeed/gradlew.bat create mode 100644 Samples/ActivityFeed/settings.gradle create mode 100644 Samples/Push/.gitignore create mode 100644 Samples/Push/build.gradle create mode 100644 Samples/Push/gradle.properties create mode 100644 Samples/Push/gradle/wrapper/gradle-wrapper.jar create mode 100644 Samples/Push/gradle/wrapper/gradle-wrapper.properties create mode 100755 Samples/Push/gradlew create mode 100644 Samples/Push/gradlew.bat create mode 100644 Samples/Push/push/.gitignore create mode 100644 Samples/Push/push/build.gradle create mode 100755 Samples/Push/push/libs/gcm.jar create mode 100644 Samples/Push/push/proguard-rules.pro create mode 100644 Samples/Push/push/src/androidTest/java/org/apache/usergrid/push/ApplicationTest.java create mode 100644 Samples/Push/push/src/main/AndroidManifest.xml create mode 100644 Samples/Push/push/src/main/java/org/apache/usergrid/push/GCMIntentService.java create mode 100644 Samples/Push/push/src/main/java/org/apache/usergrid/push/MainActivity.java create mode 100644 Samples/Push/push/src/main/java/org/apache/usergrid/push/SettingsActivity.java create mode 100644 Samples/Push/push/src/main/res/drawable/info.png create mode 100644 Samples/Push/push/src/main/res/drawable/usergridguy.png create mode 100644 Samples/Push/push/src/main/res/layout/activity_main.xml create mode 100644 Samples/Push/push/src/main/res/layout/activity_settings.xml create mode 100644 Samples/Push/push/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 Samples/Push/push/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 Samples/Push/push/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 Samples/Push/push/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 Samples/Push/push/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 Samples/Push/push/src/main/res/values-w820dp/dimens.xml create mode 100644 Samples/Push/push/src/main/res/values/colors.xml create mode 100644 Samples/Push/push/src/main/res/values/dimens.xml create mode 100644 Samples/Push/push/src/main/res/values/strings.xml create mode 100644 Samples/Push/push/src/main/res/values/styles.xml create mode 100644 Samples/Push/push/src/test/java/org/apache/usergrid/push/ExampleUnitTest.java create mode 100644 Samples/Push/settings.gradle create mode 100644 UsergridAndroidSDK/.gitignore create mode 100644 UsergridAndroidSDK/build.gradle create mode 100644 UsergridAndroidSDK/gradle/wrapper/gradle-wrapper.jar create mode 100644 UsergridAndroidSDK/gradle/wrapper/gradle-wrapper.properties create mode 100644 UsergridAndroidSDK/gradlew create mode 100644 UsergridAndroidSDK/gradlew.bat create mode 100644 UsergridAndroidSDK/libs/usergrid-java-client-2.1.0.jar create mode 100644 UsergridAndroidSDK/proguard-rules.pro create mode 100644 UsergridAndroidSDK/src/androidTest/java/org/apache/usergrid/android/ApplicationTest.java create mode 100644 UsergridAndroidSDK/src/androidTest/java/org/apache/usergrid/android/Book.java create mode 100644 UsergridAndroidSDK/src/main/AndroidManifest.xml create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/UsergridAsync.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/UsergridEntityAsync.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/UsergridResponseAsync.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/UsergridSharedDevice.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/UsergridUserAsync.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/callbacks/UsergridCheckAvailabilityCallback.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/callbacks/UsergridResponseCallback.java create mode 100644 UsergridAndroidSDK/src/main/java/org/apache/usergrid/android/tasks/UsergridAsyncTask.java create mode 100644 UsergridAndroidSDK/src/main/res/values/strings.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22ffac4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea +target/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..34193ba --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. The ASF licenses this file to You + * 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. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + * + */ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a45b8d7 --- /dev/null +++ b/README.md @@ -0,0 +1,590 @@ +# Usergrid Android SDK + +Usergrid SDK written for Android. + +The Android SDK is an extension upon the the Usergrid Java SDK with added functionality including asynchronous HTTP handling and push notifications. + +## Initialization + +There are two different ways of initializing the Usergrid Android SDK: + +1. The singleton pattern is both convenient and enables the developer to use a globally available and always-initialized instance of Usergrid. + +```java +Usergrid.initSharedInstance("orgId", "appId"); +``` + +2. The Instance pattern enables the developer to manage instances of the Usergrid client independently and in an isolated fashion. The primary use-case for this is when an application connects to multiple Usergrid targets. + +```java +UsergridClient client = new UsergridClient("orgId","appId"); +``` + +_Note: Examples in this readme assume you are using the `Usergrid` shared instance. If you've implemented the instance pattern instead, simply replace `Usergrid` with your client instance variable._ + +## ASYNCHRONOUS operations + +The examples in this readme utilize the synchronous method calls provided by the Usergrid Java SDK. + +Each RESTful operation has a matching asynchrous method within to use within the SDK for convience of use within Android applications. + +For examples of asynchronous calls, look over the sample Android applications located within the `Samples` folder. + +## RESTful operations + +When making any RESTful call, a `type` parameter (or `path`) is always required. Whether you specify this as an argument or in an object as a parameter is up to you. + +### GET + +- To get entities in a collection: + +```java +UsergridResponse response = Usergrid.GET("collection"); +List entities = response.getEntities(); +``` + +- To get a specific entity in a collection by uuid or name: + +```java +UsergridResponse response = Usergrid.GET("collection",""); +UsergridEntity entities = response.entity(); +``` + +- To get specific entities in a collection by passing a `UsergridQuery` object: + +```java +UsergridQuery query = new UsergridQuery("cats").gt("weight", 2.4) + .contains("color", "bl*") + .not() + .eq("color", "blue") + .or() + .eq("color", "orange"); + +// this will build out the following query: +// select * where weight > 2.4 and color contains 'bl*' and not color = 'blue' or color = 'orange' + +UsergridResponse response = Usergrid.GET(query); +List entities = response.getEntities(); +``` + +### POST and PUT + +POST and PUT requests both require a JSON body payload. You can pass either a Java object or a `UsergridEntity` instance. While the former works in principle, best practise is to use a `UsergridEntity` wherever practical. When an entity has a uuid or name property and already exists on the server, use a PUT request to update it. If it does not, use POST to create it. + +- To create a new entity in a collection (**POST**): + +```java +HashMap propertyMap = new HashMap<>(); +propertyMap.put("cuisine","pizza"); +UsergridEntity entity = new UsergridEntity("restaurant","Dino's Deep Dish", propertyMap); +Usergrid.POST(entity); // entity should now have a uuid property and be created + +// you can also POST an array of entities: + +HashMap propertyMap = new HashMap<>(); +propertyMap.put("cuisine","pizza"); + +ArrayList entities = new ArrayList<>(); +entities.add(new UsergridEntity("restaurant","Dino's Deep Dish", propertyMap)); +entities.add(new UsergridEntity("restaurant","Pizza da Napoli", propertyMap)); +UsergridResponse response = Usergrid.POST(entities); +List responseEntities = response.getEntities(); // responseEntities should now contain now valid posted entities. +``` + +- To update an entity in a collection (**PUT**): + +```java +HashMap propertyMap = new HashMap<>(); +propertyMap.put("cuisine","pizza"); +UsergridEntity entity = new UsergridEntity("restaurant","Dino's Deep Dish", propertyMap); +UsergridResponse response = Usergrid.POST(entity); +if( response.ok() ) { + entity.putProperty("owner","Mia Carrara"); + Usergrid.PUT(entity); // entity now has the property 'owner' +} + +// or update a set of entities by passing a UsergridQuery object + +HashMap propertiesToUpdate = new HashMap<>(); +propertiesToUpdate.put("cuisine","pizza"); +UsergridQuery query = new UsergridQuery("restaurants").eq("cuisine","italian"); + +UsergridResponse response = Usergrid.PUT(query, propertiesToUpdate); + + /* the first 10 entities matching this query criteria will be updated: + e.g.: + [ + { + "type": "restaurant", + "restaurant": "Il Tarazzo", + "cuisine": "italian", + "keywords": ["pasta"] + }, + { + "type": "restaurant", + "restaurant": "Cono Sur Pizza & Pasta", + "cuisine": "italian", + "keywords": ["pasta"] + } + ] + */ +``` + +### DELETE + +DELETE requests require either a specific entity or a `UsergridQuery` object to be passed as an argument. + +- To delete a specific entity in a collection by uuid or name: + +```java +UsergridResponse response = Usergrid.DELETE("collection", ""); // if successful, entity will now be deleted +``` + +- To specific entities in a collection to delete by passing a `UsergridQuery` object: + +```java +UsergridQuery query = new UsergridQuery("cats").eq("color","black").or().eq("color","white"); + +// this will build out the following query: +// select * where color = 'black' or color = 'white' + +UsergridResponse response = Usergrid.DELETE(query); // the first 10 entities matching this query criteria will be deleted +``` + +## Entity operations and convenience methods + +`UsergridEntity` has a number of helper/convenience methods to make working with entities more convenient. + +### reload() + +Reloads the entity from the server: + +```java +entity.reload(); // entity is now reloaded from the server +``` + +### save() + +Saves (or creates) the entity on the server: + + +```java +entity.putProperty("aNewProperty","A new value"); +entity.save(); // entity is now updated on the server +``` + +### remove() + +Deletes the entity from the server: + +```java +entity.remove(); // entity is now deleted on the server and the local instance should be destroyed +``` + +## Authentication, current user, and auth-fallback + +### appAuth and authenticateApp() + +`Usergrid` can use the app client ID and secret that were passed upon initialization and automatically retrieve an app-level token for these credentials. + +```java +Usergrid.setAppAuth(new UsergridAppAuth("", "")); +Usergrid.authenticateApp(); // Usergrid.appAuth is authenticated automatically when this call is successful +``` + +### currentUser, userAuth, and authenticateUser() + +`Usergrid` has a special `currentUser` property. + +By default, when calling `authenticateUser()`, `.currentUser` will be set to this user if the authentication flow is successful. + +```java +UsergridUserAuth userAuth = new UsergridUserAuth("",""); +Usergrid.authenticateUser(userAuth); // Usergrid.currentUser is set to the authenticated user and the token is stored within that context +``` + +If you want to utilize authenticateUser without setting as the current user, simply pass a `false` boolean value as the second parameter: + +```java +UsergridUserAuth userAuth = new UsergridUserAuth("",""); +Usergrid.authenticateUser(userAuth,false); // user is authenticated but Usergrid.currentUser is not set. +``` + +### authMode + +Auth-mode defines what the client should pass in for the authorization header. + +By default, `Usergrid.authMode` is set to `.User`, when a `Usergrid.currentUser` is present and authenticated, an API call will be performed using the token for the user. + +If `Usergrid.authMode` is set to `.None`, all API calls will be performed unauthenticated. + +If instead `Usergrid.authMode` is set to `.App`, the API call will instead be performed using client credentials, _if_ they're available (i.e. `authenticateApp()` was performed at some point). + +### usingAuth() + +At times it is desireable to have complete, granular control over the authentication context of an API call. + +To facilitate this, the passthrough function `.usingAuth()` allows you to pre-define the auth context of the next API call. + +```java +// assume Usergrid.authMode = UsergridAuthMode.NONE. + +Map permissionsMap = new HashMap<>(); +permissionsMap.put("permission","get,post,put,delete:/**"); +UsergridResponse response = Usergrid.usingAuth(Usergrid.getAppAuth()).POST("roles/guest/permissions",permissionsMap); + +// here we've temporarily used the client credentials to modify permissions +// subsequent calls will not use this auth context +``` + +## User operations and convenience methods + +`UsergridUser` has a number of helper/convenience methods to make working with user entities more convenient. If you are _not_ utilizing the `Usergrid` shared instance, you must pass an instance of `UsergridClient` as the first argument to any of these helper methods. + +### create() + +Creating a new user: + +```java +UsergridUser user = new UsergridUser("username","password"); +user.create(); // user has now been created and should have a valid uuid +``` + +### login() + +A simpler means of retrieving a user-level token: + +```java +user.login("username","password"); // user is now logged in +``` + +### logout() + +Logs out the selected user. You can also use this convenience method on `Usergrid.currentUser`. + +```java +user.logout(); // user is now logged out +``` + +### resetPassword() + +Resets the password for the selected user. + +```java +// if it was done correctly, the new password will be changed +user.resetPassword("oldPassword", "newPassword"); +``` + +### UsergridUser.CheckAvailable() + +This is a class (static) method that allows you to check whether a username or email address is available or not. + +```java +boolean available = UsergridUser.checkAvailable("email", null); // 'available' == whether an email already exists for a user + +available = UsergridUser.checkAvailable(null, "username"); // 'available' == whether an username already exists for a user + +available = UsergridUser.checkAvailable("email", "username"); // 'available' == whether an email or username already exist for a user +``` + +## Querying and filtering data + +### UsergridQuery initialization + +The `UsergridQuery` class allows you to build out complex query filters using the Usergrid [query syntax](http://docs.apigee.com/app-services/content/querying-your-data). + +The first parameter of the `UsergridQuery` builder pattern should be the collection (or type) you intend to query. You can either pass this as an argument, or as the first builder object: + +```java +UsergridQuery query = new UsergridQuery("cats"); +// or +UsergridQuery query = new UsergridQuery().collection("cats"); +``` + +You then can layer on additional queries: + +```java +UsergridQuery query = new UsergridQuery("cats").gt("weight",2.4).contains("color","bl*") + .not() + .eq("color","white") + .or() + .eq("color","orange"); +``` + +You can also adjust the number of results returned: + +```java +UsergridQuery query = new UsergridQuery("cats").eq("color","black").limit(100); + +// returns a maximum of 100 entities +``` + +And sort the results: + +```java +UsergridQuery query = new UsergridQuery("cats").eq("color","black").limit(100).asc("name") + +// sorts by 'name', ascending +``` + +And you can do geo-location queries: + +```java +UsergridQuery query = new UsergridQuery("devices").locationWithin(, , ); +``` + +### Using a query in a request + +Queries can be passed as parameters to GET, PUT, and DELETE requests: + +```java +// Gets entities matching the query. +Usergrid.GET(query); + +// Updates the entities matching the query with the new property. +Usergrid.PUT(query, Collections.singletonMap("aNewProperty","A new value")); + +// Deletes entities of a given type matching the query. +Usergrid.DELETE(query); +``` +### List of query builder objects + +`type("string")` + +> The collection name to query + +`collection("string")` + +> An alias for `type` + +`eq("key","value")` or +`equals("key","value")` or +`filter("key","value")` + +> Equal to (e.g. `where color = 'black'`) + +`contains("key","value")` or +`containsString("key","value")` or +`containsWord("key","value")` + +> Contains a string (e.g.` where color contains 'bl*'`) + +`gt("key","value")` or +`greaterThan("key","value")` + +> Greater than (e.g. `where weight > 2.4`) + +`gte("key","value")` or +`greaterThanOrEqual("key","value")` + +> Greater than or equal to (e.g. `where weight >= 2.4`) + +`lt("key","value")` or `lessThan("key","value")` + +> Less than (e.g. `where weight < 2.4`) + +`lte("key","value")` or `lessThanOrEqual("key","value")` + +> Less than or equal to (e.g. `where weight <= 2.4`) + +`not()` + +> Negates the next block in the builder pattern, e.g.: + +```java +UsergridQuery query = new UsergridQuery("cats").not().eq("color","black"); +// select * from cats where not color = 'black' +``` + +`and()` + +> Joins two queries by requiring both of them. `and` is also implied when joining two queries _without_ an operator. E.g.: + +```java +UsergridQuery query = new UsergridQuery("cats").eq("color","black").eq("fur","longHair"); +// is identical to: +UsergridQuery query = new UsergridQuery("cats").eq("color","black").and().eq("fur","longHair"); +``` + +`or()` + +> Joins two queries by requiring only one of them. `or` is never implied. e.g.: + +```java +UsergridQuery query = new UsergridQuery("cats").eq("color","black").or().eq("color", "white"); +``` + +> When using `or()` and `and()` operators, `and()` joins will take precedence over `or()` joins. You can read more about query operators and precedence [here](http://docs.apigee.com/api-baas/content/supported-query-operators-data-types). + +`locationWithin(distanceInMeters, latitude, longitude)` + +> Returns entities which have a location within the specified radius. Arguments can be `float` or `int`. + +`asc("key")` or `ascending("key")` + +> Sorts the results by the specified property, ascending + +`desc("key")` or `descending("key")` + +> Sorts the results by the specified property, descending + +`sort("key",UsergridQuerySortOrder.ASC)` + +> Sorts the results by the specified property, in the specified `UsergridQuerySortOrder` (`.ASC` or `.DESC`). + +`limit(int)` + +> The maximum number of entities to return + +`cursor("string")` + +> A pagination cursor string + +`fromString("query string")` + +> A special builder property that allows you to input a pre-defined query string. All builder properties will be ignored when this property is defined. For example: + +```java +UsergridQuery query = new UsergridQuery().fromString("select * where color = 'black' order by name asc"); +``` + +## UsergridResponse object + +`UsergridResponse` is the core class that handles both successful and unsuccessful HTTP responses from Usergrid. + +If a request is successful, any entities returned in the response will be automatically parsed into `UsergridEntity` objects and pushed to the `entities` property. + +If a request fails, the `error` property will contain information about the problem encountered. + +### ok + +You can check `UsergridResponse.ok`, a `Bool` value, to see if the response was successful. Any status code `< 400` returns true. + +```java +UsergridResponse response = Usergrid.GET("collection"); +if( response.ok() ) { + // woo! +} +``` + +### entity, entities, user, users, first, last + +Depending on the call you make, any entities returned in the response will be automatically parsed into `UsergridEntity` objects and pushed to the `entities` property. If you're querying the `users` collection, these will also be `UsergridUser` objects, a subclass of `UsergridEntity`. + +- `.first()` returns the first entity in an array of entities; `.entity()` is an alias to `.first()`. If there are no entities, both of these will be undefined. + +- `.last()` returns the last entity in an array of entities; if there is only one entity in the array, this will be the same as `.first()` _and_ `.entity()`, and will be undefined if there are no entities in the response. + +- `.getEntities()` will either be an array of entities in the response, or an empty array. + +- `.user()` is a special alias for `.entity()` for when querying the `users()` collection. Instead of being a `UsergridEntity`, it will be its subclass, `UsergridUser`. + +- `.users()` is the same as `.user()`, though behaves as `.getEntities()` does by returning either an array of UsergridUser objects or an empty array. + +Examples: + +```java +UsergridResponse response = Usergrid.GET("collection"); + // you can access: + // response.getEntities() (the returned entities) + // response.first() (the first entity) + // response.entity() (same as response.first) + // response.last() (the last entity returned) + +UsergridResponse response = Usergrid.GET("collection",""); + // you can access: + // response.entity() (the returned entity) + // response.getEntities() (containing only the returned entity) + // response.first() (same as response.entity) + // response.last() (same as response.entity) + +UsergridResponse response = Usergrid.GET("users"); + // you can access: + // response.users() (the returned users) + // response.getEntities() (same as response.users) + // response.user() (the first user) + // response.entity() (same as response.user) + // response.first() (same as response.user) + // response.last() (the last user) + +UsergridResponse response = Usergrid.GET("users",""); + // you can access; + // response.users() (containing only the one user) + // response.getEntities() (same as response.users) + // response.user() (the returned user) + // response.entity() (same as response.user) + // response.first() (same as response.user) + // response.last() (same as response.user) +``` + +## Connections + +Connections can be managed using `Usergrid.connect()`, `Usergrid.disconnect()`, and `Usergrid.getConnections()`, or entity convenience methods of the same name. + +When retrieving connections via `Usergrid.getConnections()`, you can pass in a optional `UsergridQuery` object in order to filter the connectioned entities returned. + +### Connect + +Create a connection between two entities: + +```java +Usergrid.connect(entity1, "relationship", entity2); // entity1 now has an outbound connection to entity2 +``` + +### Retrieve Connections + +Retrieve outbound connections: + +```java +Usergrid.getConnections(UsergridDirection.OUT, entity1, "relationship"); + // entities is an array of entities that entity1 is connected to via 'relationship' + // in this case, we'll see entity2 in the array +``` + +Retrieve inbound connections: + +```java +Usergrid.getConnections(UsergridDirection.IN, entity2, "relationship"); + // entities is an array of entities that connect to entity2 via 'relationship' + // in this case, we'll see entity1 in the array +``` + +### Disconnect + +Delete a connection between two entities: + +```java +Usergrid.disconnect(entity1, "relationship", entity2); + // entity1's outbound connection to entity2 has been destroyed +``` + +## Custom UsergridEntity Subclasses + +Creating custom subclasses of the base `UsergridEntity` class (just like `UsergridUser` and `UsergridDevice`) is possible. + +- To do so, subclass `UsergridEntity` and implement the required methods: + +```java +public class ActivityEntity extends UsergridEntity { + public static final String ACTIVITY_ENTITY_TYPE = "activity"; + + public ActivityEntity(){ + super(ACTIVITY_ENTITY_TYPE); + } +} +``` +- You will also need to register the custom subclass: + +```java +Usergrid.initSharedInstance("orgId","appId"); +UsergridEntity.mapCustomSubclassToType("activity", ActivityEntity.class); +``` + +By registering your custom subclass, the `UsergridEntity` and `UsergridResponse` classes are able to generate instances of these classes based on the an entities `type`. + +In the above example, entities which have a `type` value of `activity` can now be cast as `ActivityEntity` objects. e.g.: + +```java +UsergridResponse response = Usergrid.GET("activity"); +ActivityEntity activityEntity = (ActivityEntity)response.entity(); +``` diff --git a/Samples/ActivityFeed/.gitignore b/Samples/ActivityFeed/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/Samples/ActivityFeed/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/Samples/ActivityFeed/activityfeed/.gitignore b/Samples/ActivityFeed/activityfeed/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Samples/ActivityFeed/activityfeed/build.gradle b/Samples/ActivityFeed/activityfeed/build.gradle new file mode 100644 index 0000000..91d4c7e --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "org.apache.usergrid.activityfeed" + minSdkVersion 17 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.3.0' + compile project(':UsergridAndroidSDK') +} diff --git a/Samples/ActivityFeed/activityfeed/libs/gcm.jar b/Samples/ActivityFeed/activityfeed/libs/gcm.jar new file mode 100755 index 0000000000000000000000000000000000000000..ac109a830ebe95e89e1456e23328212ad70576dc GIT binary patch literal 13662 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 K2s`$_e*GU@T3ng{ literal 0 HcmV?d00001 diff --git a/Samples/ActivityFeed/activityfeed/proguard-rules.pro b/Samples/ActivityFeed/activityfeed/proguard-rules.pro new file mode 100644 index 0000000..73ed137 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/ApigeeCorporation/Developer/android_sdk_files/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Samples/ActivityFeed/activityfeed/src/androidTest/java/org/apache/usergrid/activityfeed/ApplicationTest.java b/Samples/ActivityFeed/activityfeed/src/androidTest/java/org/apache/usergrid/activityfeed/ApplicationTest.java new file mode 100644 index 0000000..b376e2f --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/androidTest/java/org/apache/usergrid/activityfeed/ApplicationTest.java @@ -0,0 +1,13 @@ +package org.apache.usergrid.activityfeed; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/Samples/ActivityFeed/activityfeed/src/main/AndroidManifest.xml b/Samples/ActivityFeed/activityfeed/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ec275c8 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/ActivityEntity.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/ActivityEntity.java new file mode 100644 index 0000000..4c19085 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/ActivityEntity.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import org.apache.usergrid.java.client.model.UsergridEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; + +@SuppressWarnings("unused") +public class ActivityEntity extends UsergridEntity { + + public static final String ACTIVITY_ENTITY_TYPE = "activity"; + + private String content; + private JsonNode actor; + + @Nullable @JsonIgnore + public String getDisplayName() { + if( actor != null ) { + JsonNode displayName = actor.get("displayName"); + if( displayName != null ) { + return displayName.asText(); + } + } + return null; + } + + @Nullable public String getContent() { return this.content; } + public void setContent(@NonNull String content) { + this.content = content; + } + + @Nullable public JsonNode getActor() { return this.actor; } + public void setActor(@NonNull JsonNode actor) { + this.actor = actor; + } + + public ActivityEntity() { + super(ACTIVITY_ENTITY_TYPE); + } + + public ActivityEntity(@JsonProperty("type") @NotNull String type) { + super(type); + } + + public ActivityEntity(@NonNull final String displayName, @NonNull final String email, @Nullable final String picture, @NonNull final String content) { + super(ACTIVITY_ENTITY_TYPE); + HashMap actorMap = new HashMap<>(); + actorMap.put("displayName",displayName); + actorMap.put("email",email); + if( picture != null ) { + HashMap imageMap = new HashMap<>(); + imageMap.put("url",picture); + imageMap.put("height",80); + imageMap.put("width",80); + actorMap.put("image",imageMap); + } + HashMap activityMap = new HashMap<>(); + activityMap.put("verb","post"); + activityMap.put("actor",actorMap); + activityMap.put("content",content); + this.putProperties(activityMap); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/GCMIntentService.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/GCMIntentService.java new file mode 100644 index 0000000..36cb9f1 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/GCMIntentService.java @@ -0,0 +1,87 @@ +package org.apache.usergrid.activityfeed; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.google.android.gcm.GCMBaseIntentService; + +import org.apache.usergrid.activityfeed.activities.MainActivity; + +public class GCMIntentService extends GCMBaseIntentService { + + public GCMIntentService() { + super(UsergridManager.GCM_SENDER_ID); + } + + @Override + protected void onRegistered(Context context, String registrationId) { + Log.i(TAG, "Device registered: " + registrationId); + UsergridManager.registerPush(context,registrationId); + } + + @Override + protected void onUnregistered(Context context, String registrationId) { + Log.i(TAG, "Device unregistered"); + } + + @Override + protected void onMessage(Context context, Intent intent) { + String message = intent.getExtras().getString("data"); + Log.i(TAG, "Received message: " + message); + generateNotification(context, message); + } + + @Override + protected void onDeletedMessages(Context context, int total) { + Log.i(TAG, "Received deleted messages notification"); + String message = "GCM server deleted " + total +" pending messages!"; + generateNotification(context, message); + } + + @Override + public void onError(Context context, String errorId) { + Log.i(TAG, "Received error: " + errorId); + } + + @Override + protected boolean onRecoverableError(Context context, String errorId) { + Log.i(TAG, "Received recoverable error: " + errorId); + return super.onRecoverableError(context, errorId); + } + + /** + * Issues a Notification to inform the user that server has sent a message. + */ + private static void generateNotification(Context context, String message) { + long when = System.currentTimeMillis(); + NotificationManager notificationManager = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + + Intent notificationIntent = new Intent(context, MainActivity.class); + // set intent so it does not start a new activity + notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0); + + Notification notification = new NotificationCompat.Builder(context) + .setContentText(message) + .setContentTitle(context.getString(R.string.app_name)) + .setWhen(when) + .setSmallIcon(R.drawable.usergridguy) + .setContentIntent(intent) + .build(); + + notification.flags |= Notification.FLAG_AUTO_CANCEL; + + // Play default notification sound + notification.defaults |= Notification.DEFAULT_SOUND; + + // Vibrate if vibrate is enabled + notification.defaults |= Notification.DEFAULT_VIBRATE; + notificationManager.notify(0, notification); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/UsergridManager.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/UsergridManager.java new file mode 100644 index 0000000..b201631 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/UsergridManager.java @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.google.android.gcm.GCMRegistrar; + +import org.apache.usergrid.activityfeed.activities.FeedActivity; +import org.apache.usergrid.activityfeed.callbacks.GetFeedMessagesCallback; +import org.apache.usergrid.activityfeed.callbacks.PostFeedMessageCallback; +import org.apache.usergrid.activityfeed.helpers.AlertDialogHelpers; +import org.apache.usergrid.android.UsergridAsync; +import org.apache.usergrid.android.UsergridSharedDevice; +import org.apache.usergrid.android.UsergridUserAsync; +import org.apache.usergrid.android.callbacks.UsergridResponseCallback; +import org.apache.usergrid.java.client.Usergrid; +import org.apache.usergrid.java.client.UsergridEnums; +import org.apache.usergrid.java.client.UsergridRequest; +import org.apache.usergrid.java.client.auth.UsergridUserAuth; +import org.apache.usergrid.java.client.model.UsergridEntity; +import org.apache.usergrid.java.client.model.UsergridUser; +import org.apache.usergrid.java.client.query.UsergridQuery; +import org.apache.usergrid.java.client.response.UsergridResponse; +import org.apache.usergrid.java.client.response.UsergridResponseError; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public final class UsergridManager { + + private static final String ORG_ID = "rwalsh"; + private static final String APP_ID = "sandbox"; + private static final String BASE_URL = "https://api.usergrid.com"; + private static final String ANDROID_NOTIFIER_ID = "androidPushNotifier"; + + public static String GCM_SENDER_ID = "186455511595"; + public static String GCM_REGISTRATION_ID = ""; + + private UsergridManager() {} + + public static void initializeSharedInstance(@NonNull final Context context) { + Usergrid.initSharedInstance(ORG_ID,APP_ID,BASE_URL); + Usergrid.setAuthMode(UsergridEnums.UsergridAuthMode.USER); + UsergridEntity.mapCustomSubclassToType(ActivityEntity.ACTIVITY_ENTITY_TYPE,ActivityEntity.class); + UsergridSharedDevice.saveSharedDevice(context, new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { } + }); + registerPush(context); + } + + public static void registerPush(Context context) { + final String regId = GCMRegistrar.getRegistrationId(context); + if ("".equals(regId)) { + GCMRegistrar.register(context, GCM_SENDER_ID); + } else { + if (GCMRegistrar.isRegisteredOnServer(context)) { + Log.i("", "Already registered with GCM"); + } else { + registerPush(context, regId); + } + } + } + + public static void registerPush(@NonNull final Context context, @NonNull final String registrationId) { + GCM_REGISTRATION_ID = registrationId; + UsergridAsync.applyPushToken(context, registrationId, ANDROID_NOTIFIER_ID, new UsergridResponseCallback() { + @Override + public void onResponse(@NonNull UsergridResponse response) { + if( !response.ok() && response.getResponseError() != null ) { + System.out.print("Error Description :" + response.getResponseError().toString()); + } + } + }); + } + + public static void loginUser(@NonNull final Activity activity, @NonNull final String username, @NonNull final String password) { + UsergridAsync.authenticateUser(new UsergridUserAuth(username,password), new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull final UsergridResponse response) { + final UsergridUser currentUser = Usergrid.getCurrentUser(); + if( response.ok() && currentUser != null ) { + UsergridAsync.connect("users", "me", "devices", UsergridSharedDevice.getSharedDeviceUUID(activity), new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + AlertDialogHelpers.showScrollableAlert(activity,"Authenticate User Successful","User Description: \n\n " + currentUser.toPrettyString(), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + activity.startActivity(new Intent(activity,FeedActivity.class)); + } + }); + } + }); + } else { + AlertDialogHelpers.showAlert(activity,"Error Authenticating User","Invalid username or password."); + } + } + }); + } + + public static void logoutCurrentUser(@NonNull final Activity activity) { + UsergridAsync.disconnect("users", "me", "devices", UsergridSharedDevice.getSharedDevice(activity).getUuid(), new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + UsergridAsync.logoutCurrentUser(new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + System.out.print(response.toString()); + } + }); + } + }); + } + + public static void createUserAccount(@NonNull final Activity activity, @NonNull final String name, @NonNull final String username, @NonNull final String email, @NonNull final String password) { + final UsergridUser user = new UsergridUser(name,username,email,password); + UsergridUserAsync.create(user, new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + final UsergridUser responseUser = response.user(); + if( response.ok() && responseUser != null ) { + AlertDialogHelpers.showScrollableAlert(activity, "Creating Account Successful", "User Description: \n\n " + responseUser.toPrettyString(), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + activity.finish(); + } + }); + } else { + String errorMessage = "Unknown Error"; + UsergridResponseError responseError = response.getResponseError(); + if( responseError != null ) { + errorMessage = responseError.getErrorDescription(); + } + AlertDialogHelpers.showAlert(activity,"Error Creating Account",errorMessage); + } + } + }); + } + + @SuppressWarnings("unchecked") + public static void getFeedMessages(@NonNull final GetFeedMessagesCallback callback) { + final UsergridQuery feedMessagesQuery = new UsergridQuery("users/me/feed").desc(UsergridEnums.UsergridEntityProperties.CREATED.toString()); + UsergridAsync.GET(feedMessagesQuery, new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + ArrayList feedMessages = new ArrayList<>(); + if( response.ok() ) { + List feedEntities = response.getEntities(); + if( feedEntities != null ) { + Collections.reverse(feedEntities); + feedMessages.addAll((List)feedEntities); + } + } + callback.onResponse(feedMessages); + } + }); + } + + public static void postFeedMessage(@NonNull final String messageText, @NonNull final PostFeedMessageCallback callback) { + final UsergridUser currentUser = Usergrid.getCurrentUser(); + if( currentUser != null ) { + String usernameOrEmail = currentUser.usernameOrEmail(); + if( usernameOrEmail == null ) { + usernameOrEmail = ""; + } + String email = currentUser.getEmail(); + if( email == null ) { + email = ""; + } + String picture = currentUser.getPicture(); + final ActivityEntity activityEntity = new ActivityEntity(usernameOrEmail,email,picture,messageText); + UsergridAsync.POST("users/me/activities",activityEntity.toMapValue(), new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + final UsergridEntity responseEntity = response.entity(); + if( response.ok() && responseEntity != null && responseEntity instanceof ActivityEntity ) { + callback.onSuccess((ActivityEntity)responseEntity); + UsergridManager.sendPushToFollowers(messageText); + } + } + }); + } + } + + public static void followUser(@NonNull final Activity activity, @NonNull final String username) { + UsergridAsync.connect("users", "me", "following", "users", username, new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + if( response.ok() ) { + activity.finish(); + } else { + String errorMessage = "Unknown Error"; + UsergridResponseError responseError = response.getResponseError(); + if( responseError != null ) { + String errorDescription = responseError.getErrorDescription(); + if( errorDescription != null ) { + errorMessage = errorDescription; + } + } + AlertDialogHelpers.showAlert(activity,"Error Following User",errorMessage); + } + } + }); + } + + public static void sendPushToFollowers(@NonNull final String message) { + HashMap notificationMap = new HashMap<>(); + notificationMap.put(ANDROID_NOTIFIER_ID,message); + final HashMap> payloadMap = new HashMap<>(); + payloadMap.put("payloads",notificationMap); + + UsergridAsync.GET("users/me/followers", new UsergridResponseCallback() { + @Override + public void onResponse(@NotNull UsergridResponse response) { + if( response.ok() ) { + String followerUserNames = ""; + final List users = response.users(); + if( users != null && !users.isEmpty() ) { + for( UsergridUser user : users ) { + String username = user.getUsername(); + if( username != null && !username.isEmpty() ) { + followerUserNames += username + ";"; + } + } + if( !followerUserNames.isEmpty() ) { + final UsergridRequest notificationRequest = new UsergridRequest(UsergridEnums.UsergridHttpMethod.POST,UsergridRequest.APPLICATION_JSON_MEDIA_TYPE,Usergrid.clientAppUrl(),null,payloadMap,Usergrid.authForRequests(),"users", followerUserNames, "notifications"); + UsergridAsync.sendRequest(notificationRequest, new UsergridResponseCallback() { + @Override + public void onResponse(@NonNull UsergridResponse response) {} + }); + } + } + } + } + }); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/CreateAccountActivity.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/CreateAccountActivity.java new file mode 100644 index 0000000..0f72fbb --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/CreateAccountActivity.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.activities; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import org.apache.usergrid.activityfeed.R; +import org.apache.usergrid.activityfeed.UsergridManager; +import org.apache.usergrid.activityfeed.helpers.ActionBarHelpers; +import org.apache.usergrid.activityfeed.helpers.AlertDialogHelpers; + +public class CreateAccountActivity extends AppCompatActivity { + + private static final String actionBarTitle = "Create Account"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_create_account); + + ActionBarHelpers.setCustomViewForActionBarWithTitle(this,actionBarTitle); + + final EditText nameText = (EditText) findViewById(R.id.nameText); + final EditText usernameEditText = (EditText) findViewById(R.id.usernameText); + final EditText emailText = (EditText) findViewById(R.id.emailText); + final EditText passwordEditText = (EditText) findViewById(R.id.passwordEditText); + + final TextView createAccountTextView = (TextView) findViewById(R.id.createAccountText); + if( createAccountTextView != null ) { + createAccountTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( nameText != null && usernameEditText != null && emailText != null && passwordEditText != null ) { + String name = nameText.getText().toString(); + String username = usernameEditText.getText().toString(); + String email = emailText.getText().toString(); + String password = passwordEditText.getText().toString(); + if(!name.isEmpty() && !username.isEmpty() && !email.isEmpty() && !password.isEmpty()) { + UsergridManager.createUserAccount(CreateAccountActivity.this,name,username,email,password); + } else { + AlertDialogHelpers.showAlert(CreateAccountActivity.this,"Error Creating Account","All fields must not be empty."); + } + } + + } + }); + } + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FeedActivity.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FeedActivity.java new file mode 100644 index 0000000..4f347f8 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FeedActivity.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; + +import org.apache.usergrid.activityfeed.ActivityEntity; +import org.apache.usergrid.activityfeed.R; +import org.apache.usergrid.activityfeed.UsergridManager; +import org.apache.usergrid.activityfeed.callbacks.GetFeedMessagesCallback; +import org.apache.usergrid.activityfeed.callbacks.PostFeedMessageCallback; +import org.apache.usergrid.activityfeed.helpers.ActionBarHelpers; +import org.apache.usergrid.activityfeed.helpers.FeedAdapter; +import org.apache.usergrid.java.client.Usergrid; +import org.apache.usergrid.java.client.model.UsergridUser; + +import java.util.ArrayList; +import java.util.List; + +public class FeedActivity extends AppCompatActivity { + + private EditText messageET; + private ListView messagesContainer; + private FeedAdapter adapter; + private ArrayList feedMessages; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_feed); + + final UsergridUser currentUser = Usergrid.getCurrentUser(); + String username = "Unknown"; + if( currentUser != null ) { + String currentUsername = currentUser.getUsername(); + if( currentUsername != null ) { + username = currentUser.getUsername(); + } + } + final Intent followActivityIntent = new Intent(this,FollowActivity.class); + ActionBarHelpers.setCustomViewForActionBarWithTitle(this, username + "'s feed", "Follow", new View.OnClickListener() { + @Override + public void onClick(View v) { + FeedActivity.this.startActivity(followActivityIntent); + } + }); + + initControls(); + } + + private void initControls() { + messagesContainer = (ListView) findViewById(R.id.messagesContainer); + messageET = (EditText) findViewById(R.id.messageEdit); + if(messageET != null) { + messageET.setMaxWidth(messageET.getWidth()); + } + final Button sendBtn = (Button) findViewById(R.id.chatSendButton); + if( sendBtn != null ) { + sendBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String messageText = messageET.getText().toString(); + if (TextUtils.isEmpty(messageText)) { + return; + } + + UsergridManager.postFeedMessage(messageText, new PostFeedMessageCallback() { + @Override + public void onSuccess(@NonNull ActivityEntity activityEntity) { + displayMessage(activityEntity); + } + }); + messageET.setText(""); + } + }); + } + } + + private void displayMessage(ActivityEntity message) { + adapter.add(message); + adapter.notifyDataSetChanged(); + scroll(); + } + + private void scroll() { + messagesContainer.setSelection(messagesContainer.getCount() - 1); + } + + @SuppressWarnings("unchecked") + private void loadMessages(){ + + feedMessages = new ArrayList<>(); + adapter = new FeedAdapter(FeedActivity.this, new ArrayList()); + messagesContainer.setAdapter(adapter); + + UsergridManager.getFeedMessages(new GetFeedMessagesCallback() { + @Override + public void onResponse(@NonNull List feedMessages) { + FeedActivity.this.feedMessages.addAll(feedMessages); + for( ActivityEntity activityEntity : FeedActivity.this.feedMessages ) { + displayMessage(activityEntity); + } + } + }); + } + + @Override + protected void onResume() { + this.loadMessages(); + super.onResume(); + } + + @Override + protected void onDestroy() { + if( Usergrid.getCurrentUser() != null ) { + UsergridManager.logoutCurrentUser(this); + } + super.onDestroy(); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FollowActivity.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FollowActivity.java new file mode 100644 index 0000000..c4aac27 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/FollowActivity.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.activities; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import org.apache.usergrid.activityfeed.R; +import org.apache.usergrid.activityfeed.UsergridManager; +import org.apache.usergrid.activityfeed.helpers.ActionBarHelpers; +import org.apache.usergrid.activityfeed.helpers.AlertDialogHelpers; + +public class FollowActivity extends AppCompatActivity { + + private static final String actionBarTitle = "Follow"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_follow); + + ActionBarHelpers.setCustomViewForActionBarWithTitle(this,actionBarTitle); + + final EditText usernameEditText = (EditText) findViewById(R.id.followUsernameText); + final Button addFollowerButton = (Button) findViewById(R.id.addFollowerButton); + if( addFollowerButton != null ) { + addFollowerButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( usernameEditText != null ) { + final String username = usernameEditText.getText().toString(); + if( !username.isEmpty() ) { + UsergridManager.followUser(FollowActivity.this,username); + } else { + AlertDialogHelpers.showAlert(FollowActivity.this,"Error Following User","Please enter a valid username."); + } + } + } + }); + } + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/MainActivity.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/MainActivity.java new file mode 100644 index 0000000..338f829 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/activities/MainActivity.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import org.apache.usergrid.activityfeed.R; +import org.apache.usergrid.activityfeed.UsergridManager; +import org.apache.usergrid.activityfeed.helpers.AlertDialogHelpers; +import org.apache.usergrid.java.client.Usergrid; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + UsergridManager.initializeSharedInstance(this); + + final EditText usernameEditText = (EditText) findViewById(R.id.usernameText); + if( usernameEditText != null ) { + usernameEditText.setSelection(usernameEditText.getText().length()); + } + final EditText passwordEditText = (EditText) findViewById(R.id.passwordEditText); + if( passwordEditText != null ) { + passwordEditText.setSelection(passwordEditText.getText().length()); + } + + final Button signInButton = (Button) findViewById(R.id.signInButton); + if( signInButton != null ) { + signInButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( usernameEditText != null && passwordEditText != null ) { + final String username = usernameEditText.getText().toString(); + final String password = passwordEditText.getText().toString(); + if( !username.isEmpty() && !password.isEmpty() ) { + UsergridManager.loginUser(MainActivity.this,username,password); + } else { + AlertDialogHelpers.showAlert(MainActivity.this,"Error Authenticating User","Username and password must not be empty."); + } + } + } + }); + } + + final TextView createAccountTextView = (TextView) findViewById(R.id.createAccountTextView); + if( createAccountTextView != null ) { + final Intent createAccountIntent = new Intent(this,CreateAccountActivity.class); + createAccountTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + MainActivity.this.startActivity(createAccountIntent); + } + }); + } + } + + @Override + protected void onResume() { + if(Usergrid.getCurrentUser() != null) { + this.startActivity(new Intent(this,FeedActivity.class)); + } + super.onResume(); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/GetFeedMessagesCallback.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/GetFeedMessagesCallback.java new file mode 100644 index 0000000..ae7b999 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/GetFeedMessagesCallback.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.callbacks; + +import android.support.annotation.NonNull; + +import org.apache.usergrid.activityfeed.ActivityEntity; + +import java.util.List; + +public interface GetFeedMessagesCallback { + void onResponse(@NonNull final List feedMessages); +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/PostFeedMessageCallback.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/PostFeedMessageCallback.java new file mode 100644 index 0000000..14f5117 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/callbacks/PostFeedMessageCallback.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.callbacks; + +import android.support.annotation.NonNull; + +import org.apache.usergrid.activityfeed.ActivityEntity; + +public interface PostFeedMessageCallback { + void onSuccess(@NonNull final ActivityEntity activityEntity); +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/ActionBarHelpers.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/ActionBarHelpers.java new file mode 100644 index 0000000..46c2560 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/ActionBarHelpers.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.helpers; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.View; +import android.widget.TextView; + +import org.apache.usergrid.activityfeed.R; + +public final class ActionBarHelpers { + private ActionBarHelpers() {} + + public static void setCustomViewForActionBarWithTitle(@NonNull final AppCompatActivity activity, @Nullable final String title) { + ActionBarHelpers.setCustomViewForActionBarWithTitle(activity,title,null,null); + } + + public static void setCustomViewForActionBarWithTitle(@NonNull final AppCompatActivity activity, @Nullable final String title, @Nullable final String rightButtonTitle, @Nullable final View.OnClickListener rightButtonOnClick) { + ActionBar actionBar = activity.getSupportActionBar(); + if( actionBar != null ) { + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + View actionBarView = View.inflate(activity, R.layout.action_bar_layout,null); + TextView actionBarTitleText = (TextView) actionBarView.findViewById(R.id.actionBarTitle); + actionBarTitleText.setText(title); + if( rightButtonTitle != null ) { + TextView rightTextView = (TextView) actionBarView.findViewById(R.id.buttonTitle); + rightTextView.setText(rightButtonTitle); + rightTextView.setOnClickListener(rightButtonOnClick); + } + final ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.MATCH_PARENT, Gravity.CENTER); + actionBar.setCustomView(actionBarView,params); + } + } + +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/AlertDialogHelpers.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/AlertDialogHelpers.java new file mode 100644 index 0000000..e871271 --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/AlertDialogHelpers.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.helpers; + +import android.app.Activity; +import android.content.DialogInterface; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.view.View; +import android.widget.TextView; + +import org.apache.usergrid.activityfeed.R; + +@SuppressWarnings("unused") +public final class AlertDialogHelpers { + + private AlertDialogHelpers() {} + + public static void showAlert(@NonNull final Activity activity, @Nullable final String title, @Nullable final String message) { + AlertDialogHelpers.showAlert(activity,title,message,null); + } + + public static void showAlert(@NonNull final Activity activity, @Nullable final String title, @Nullable final String message, @Nullable final DialogInterface.OnClickListener onClickListener) { + new AlertDialog.Builder(activity) + .setTitle(title) + .setMessage(message) + .setPositiveButton(android.R.string.ok, onClickListener) + .show(); + } + + public static void showScrollableAlert(@NonNull final Activity activity, @Nullable final String title, @Nullable final String message) { + AlertDialogHelpers.showScrollableAlert(activity, title, message,null); + } + + public static void showScrollableAlert(@NonNull final Activity activity, @Nullable final String title, @Nullable final String message, @Nullable final DialogInterface.OnClickListener onClickListener) { + final View scrollableAlertView = View.inflate(activity, R.layout.scrollable_alert_view, null); + final TextView titleTextView = (TextView) scrollableAlertView.findViewById(R.id.scrollableAlertTitle); + titleTextView.setText(title); + final TextView messageTextView = (TextView) scrollableAlertView.findViewById(R.id.scrollableAlertMessage); + messageTextView.setText(message); + new AlertDialog.Builder(activity) + .setView(scrollableAlertView) + .setPositiveButton(android.R.string.ok, onClickListener) + .show(); + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/FeedAdapter.java b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/FeedAdapter.java new file mode 100644 index 0000000..53494dd --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/java/org/apache/usergrid/activityfeed/helpers/FeedAdapter.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.usergrid.activityfeed.helpers; + +import android.app.Activity; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.apache.usergrid.activityfeed.ActivityEntity; +import org.apache.usergrid.activityfeed.R; +import org.apache.usergrid.java.client.Usergrid; +import org.apache.usergrid.java.client.model.UsergridUser; + +import java.util.List; + +public class FeedAdapter extends BaseAdapter { + private final List feedMessages; + private final Activity context; + + public FeedAdapter(Activity context, List feedMessages) { + this.context = context; + this.feedMessages = feedMessages; + } + + @Override + public int getCount() { + if (feedMessages != null) { + return feedMessages.size(); + } else { + return 0; + } + } + + @Override + public ActivityEntity getItem(int position) { + if (feedMessages != null) { + return feedMessages.get(position); + } else { + return null; + } + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + ViewHolder holder; + ActivityEntity messageEntity = getItem(position); + if (convertView == null) { + convertView = View.inflate(context,R.layout.message_layout,null); + holder = createViewHolder(convertView); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + holder.txtMessage.setText(messageEntity.getContent()); + + boolean isMe = false; + String displayName = messageEntity.getDisplayName(); + if( displayName != null ) { + final UsergridUser currentUser = Usergrid.getCurrentUser(); + if( currentUser != null ) { + final String currentUserUsername = currentUser.getUsername(); + if( currentUserUsername != null && displayName.equalsIgnoreCase(currentUserUsername) ) { + isMe = true; + } + } + holder.txtInfo.setText(displayName); + } + setAlignment(holder,isMe); + return convertView; + } + + public void add(ActivityEntity message) { + feedMessages.add(message); + } + + private void setAlignment(ViewHolder holder, boolean isMe) { + int gravity; + int drawableResourceId; + if( !isMe ) { + gravity = Gravity.END; + drawableResourceId = R.drawable.in_message_bg; + } else { + gravity = Gravity.START; + drawableResourceId = R.drawable.out_message_bg; + } + + holder.contentWithBG.setBackgroundResource(drawableResourceId); + + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) holder.contentWithBG.getLayoutParams(); + layoutParams.gravity = gravity; + holder.contentWithBG.setLayoutParams(layoutParams); + + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) holder.content.getLayoutParams(); + if( !isMe ) { + lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0); + lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + } else { + lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); + lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + } + + holder.content.setLayoutParams(lp); + layoutParams = (LinearLayout.LayoutParams) holder.txtMessage.getLayoutParams(); + layoutParams.gravity = gravity; + holder.txtMessage.setLayoutParams(layoutParams); + + layoutParams = (LinearLayout.LayoutParams) holder.txtInfo.getLayoutParams(); + layoutParams.gravity = gravity; + holder.txtInfo.setLayoutParams(layoutParams); + } + + private ViewHolder createViewHolder(View v) { + ViewHolder holder = new ViewHolder(); + holder.txtMessage = (TextView) v.findViewById(R.id.txtMessage); + holder.content = (LinearLayout) v.findViewById(R.id.content); + holder.contentWithBG = (LinearLayout) v.findViewById(R.id.contentWithBackground); + holder.txtInfo = (TextView) v.findViewById(R.id.txtInfo); + return holder; + } + + + private static class ViewHolder { + public TextView txtMessage; + public TextView txtInfo; + public LinearLayout content; + public LinearLayout contentWithBG; + } +} diff --git a/Samples/ActivityFeed/activityfeed/src/main/res/drawable/in_message_bg.9.png b/Samples/ActivityFeed/activityfeed/src/main/res/drawable/in_message_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..08c6f096587d5236125f17c1182219dc2e3158d9 GIT binary patch literal 1160 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 + + + + + + + diff --git a/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_create_account.xml b/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_create_account.xml new file mode 100644 index 0000000..1fba8ed --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_create_account.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + diff --git a/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_feed.xml b/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_feed.xml new file mode 100644 index 0000000..7e0789a --- /dev/null +++ b/Samples/ActivityFeed/activityfeed/src/main/res/layout/activity_feed.xml @@ -0,0 +1,46 @@ + + + + + +