├── .gitignore ├── README.md ├── android-sdk ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── ic_launcher-web.png │ │ ├── java │ │ │ └── com │ │ │ │ └── dreamfactory │ │ │ │ └── sampleapp │ │ │ │ ├── DreamFactoryApp.java │ │ │ │ ├── activities │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── ChooseImageActivity.java │ │ │ │ ├── ContactListActivity.java │ │ │ │ ├── ContactViewActivity.java │ │ │ │ ├── CreateContactActivity.java │ │ │ │ ├── EditContactActivity.java │ │ │ │ ├── GroupActivity.java │ │ │ │ ├── GroupListActivity.java │ │ │ │ └── LoginActivity.java │ │ │ │ ├── adapters │ │ │ │ ├── ContactListAdapter.java │ │ │ │ ├── CreateGroupAdapter.java │ │ │ │ ├── DeletableContactListAdapter.java │ │ │ │ ├── DeletableGroupListAdapter.java │ │ │ │ ├── EditGroupAdapter.java │ │ │ │ ├── GroupListAdapter.java │ │ │ │ └── ProfileImageChooserAdapter.java │ │ │ │ ├── api │ │ │ │ ├── DreamFactoryAPI.java │ │ │ │ └── services │ │ │ │ │ ├── AuthService.java │ │ │ │ │ ├── ContactGroupService.java │ │ │ │ │ ├── ContactInfoService.java │ │ │ │ │ ├── ContactService.java │ │ │ │ │ └── ImageService.java │ │ │ │ ├── customviews │ │ │ │ ├── EditInfoViewGroup.java │ │ │ │ └── InfoViewGroup.java │ │ │ │ ├── models │ │ │ │ ├── BaseRecord.java │ │ │ │ ├── ContactInfoRecord.java │ │ │ │ ├── ContactRecord.java │ │ │ │ ├── ContactsRelationalRecord.java │ │ │ │ ├── ErrorMessage.java │ │ │ │ ├── FileRecord.java │ │ │ │ ├── FileRequest.java │ │ │ │ ├── GroupRecord.java │ │ │ │ ├── RegisterResponse.java │ │ │ │ ├── Resource.java │ │ │ │ ├── User.java │ │ │ │ └── requests │ │ │ │ │ ├── LoginRequest.java │ │ │ │ │ └── RegisterRequest.java │ │ │ │ └── utils │ │ │ │ ├── CustomJsonDateDeserializer.java │ │ │ │ ├── ImageUtil.java │ │ │ │ └── PrefUtil.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── add_button_selector.xml │ │ │ ├── back_button_selector.xml │ │ │ ├── default_portrait.png │ │ │ ├── df_logo_filled.png │ │ │ ├── done_button_selector.xml │ │ │ ├── edit_button_selector.xml │ │ │ ├── home.png │ │ │ ├── ic_add_black_24dp.png │ │ │ ├── ic_arrow_back_black_24dp.png │ │ │ ├── ic_check_black_24dp.png │ │ │ ├── ic_delete_black_24dp.png │ │ │ ├── ic_mode_edit_black_24dp.png │ │ │ ├── mail.png │ │ │ └── phone1.png │ │ │ ├── layout │ │ │ ├── activity_choose_image.xml │ │ │ ├── activity_contact_list.xml │ │ │ ├── activity_contact_view.xml │ │ │ ├── activity_edit_contact.xml │ │ │ ├── activity_group.xml │ │ │ ├── activity_group_list.xml │ │ │ ├── activity_login.xml │ │ │ ├── contact_info_layout.xml │ │ │ ├── edit_contact_info_layout.xml │ │ │ ├── persistent_bar.xml │ │ │ ├── rowlayout.xml │ │ │ └── selcetable_row_layout.xml │ │ │ ├── menu │ │ │ ├── menu_choose_image.xml │ │ │ ├── menu_contact_list.xml │ │ │ ├── menu_contact_view.xml │ │ │ ├── menu_create_group.xml │ │ │ ├── menu_edit_contact.xml │ │ │ ├── menu_group_list.xml │ │ │ └── menu_main.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── strings_activity_login.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── dreamfactory │ │ └── sampleapp │ │ └── api │ │ └── DreamFactoryAPITest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── app-debug.apk └── package ├── add_android.dfpkg ├── data.json ├── description.json └── schema.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | /*/build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio 32 | .idea/ 33 | .gradle 34 | /*/local.properties 35 | /*/out 36 | /*/*/build 37 | /*/*/production 38 | *.iml 39 | *.iws 40 | *.ipr 41 | *~ 42 | *.swp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Address Book for Android 2 | ======================== 3 | 4 | This repo contains a sample address book application for Android that demonstrates how to use the DreamFactory REST API. It includes new user registration, user login, and CRUD for related tables. 5 | 6 | #Getting DreamFactory on your local machine 7 | 8 | To download and install DreamFactory, follow the instructions [here](http://wiki.dreamfactory.com/DreamFactory/Installation). Alternatively, you can create a [free hosted developer account](http://www.dreamfactory.com) at www.dreamfactory.com if you don't want to install DreamFactory locally. 9 | 10 | #Configuring your DreamFactory instance to run the app 11 | 12 | - Enable [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) for development purposes. 13 | - In the admin console, navigate to the Config tab and click on CORS in the left sidebar. 14 | - Click Add. 15 | - Set Origin, Paths, and Headers to *. 16 | - Set Max Age to 0. 17 | - Allow all HTTP verbs and check the Enabled box. 18 | - Click update when you are done. 19 | - More info on setting up CORS is available [here](http://wiki.dreamfactory.com/DreamFactory/Tutorials/Enabling_CORS_Access). 20 | 21 | - Create a default role for new users and enable open registration 22 | - In the admin console, click the Roles tab then click Create in the left sidebar. 23 | - Enter a name for the role and check the Active box. 24 | - Go to the Access tab. 25 | - Add a new entry under Service Access (you can make it more restrictive later). 26 | - set Service = All 27 | - set Component = * 28 | - check all HTTP verbs under Access 29 | - set Requester = API 30 | - Click Create Role. 31 | - Click the Services tab, then edit the user service. Go to Config and enable Allow Open Registration. 32 | - Set the Open Reg Role Id to the name of the role you just created. 33 | - Make sure Open Reg Email Service Id is blank, so that new users can register without email confirmation. 34 | - Save changes. 35 | 36 | - Import the package file for the app. 37 | - From the Apps tab in the admin console, click Import and click 'Address Book for Android' in the list of sample apps. The Address Book package contains the application description, schemas, and sample data. 38 | - Set storage service to files 39 | - Leave storage folder blank. 40 | - Click the Import button. If successful, your app will appear on the Apps tab. You may have to refresh the page to see your new app in the list. 41 | 42 | - Make sure you have a SQL database service named 'db'. Depending on how you installed DreamFactory you may or may not have a 'db' service already available on your instance. You can add one by going to the Services tab in the admin console and creating a new SQL service. Make sure you set the name to 'db'. 43 | 44 | #Running the Address Book app 45 | 46 | Almost there! Clone this repo to your local machine then open and run the project with Android Studio. To open the project select the android-sdk/android-sdk directory. 47 | 48 | Before running the project you need to edit apiKey in the file AndroidManifest.xml to match the key for your new app. This key can be found by selecting your app from the list on the Apps tab in the admin console. 49 | 50 | If your DreamFactory instance is on localhost, make sure the instanceUrl in AndroidManifest.xml is set to the emulator localhost IP of 10.0.2.2. Make sure the port numbers match. 51 | 52 | `````` 53 | 54 | If your instance is not on localhost, set instanceUrl to point to the proper location. 55 | 56 | `````` 57 | 58 | When the app starts up you can register a new user, or log in as an existing user. Currently the app does not support registering and logging in admin users. 59 | 60 | #Example API calls 61 | 62 | The app uses the Retrofit2 library to send REST requests. 63 | 64 | The general form of a DreamFactory REST API call is: 65 | 66 | ` http[s]:///api/v2/[]/[][?=]` 67 | 68 | An Android call looks like this: 69 | 70 | ```Java 71 | final ContactGroupService service = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 72 | 73 | service.getGroupList().enqueue(new Callback>() { 74 | @Override 75 | public void onResponse(Call> call, Response> response) { 76 | // Handle response on main thread 77 | } 78 | 79 | @Override 80 | public void onFailure(Call> call, Throwable t) { 81 | // Handle error on main thread 82 | } 83 | }); 84 | ``` 85 | 86 | More about sending REST requests using Retrofit2 library here: http://square.github.io/retrofit/ 87 | 88 | ###Database 89 | #####Selecting records (to get or delete) 90 | - Using filters 91 | ```Java 92 | 93 | // filter param should be like "contact_id=2" 94 | @GET("db/_table/contact_info") 95 | Call> getContactInfo(@Query(value = "filter") String filter); 96 | 97 | ``` 98 | 99 | - Related records 100 | ```Java 101 | // filter to only get the contact_group_relationships we want, filter should be like "contact_group_id=1" 102 | // request without related would return just {id, contact_group_id, contact_id} 103 | // set the related field to go get the contact records referenced by 104 | // each contact_group_relationship record 105 | @GET("db/_table/contact_group_relationship?related=contact_by_contact_id") 106 | Call> getGroupContacts(@Query(value = "filter") String filter); 107 | ``` 108 | 109 | - Using id_fields 110 | ```Java 111 | // since we don't know the contact_group_relationship ids, change the id field to id 112 | @GET("db/_table/contact_group_relationship?id_field=id") 113 | Call> getGroupContacts(@Body Resource ids); 114 | ``` 115 | 116 | - Records by id 117 | ```Java 118 | // delete contact records by record ids 119 | // ids is comma separated list of contact ids 120 | @DELETE("db/_table/contact") 121 | Call> removeContacts(@Query(value = "ids") String ids); 122 | ``` 123 | 124 | #####Updating records 125 | ```Java 126 | // send the record to patch, need to include record id inside each ContactRecord instance 127 | @PATCH("db/_table/contact") 128 | Call> updateContacts(@Body Resource contactRecords); 129 | ``` 130 | 131 | #####Creating records 132 | ```Java 133 | // only need to send the group name inside GroupRecord instance 134 | // we don't have a group ID yet, so we can't provide one here 135 | @POST("db/_table/contact_group") 136 | Call> createContactGroups(@Body Resource records); 137 | ``` 138 | 139 | #Additional Resources 140 | 141 | More detailed information on the DreamFactory REST API is available [here](http://wiki.dreamfactory.com/DreamFactory/API). 142 | 143 | The live API documentation included in the admin console is a great way to learn how the DreamFactory REST API works. 144 | -------------------------------------------------------------------------------- /android-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /android-sdk/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-sdk/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | useLibrary 'org.apache.http.legacy' 7 | 8 | defaultConfig { 9 | applicationId "com.dreamfactory.android_sdk" 10 | minSdkVersion 15 11 | targetSdkVersion 23 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | packagingOptions { 22 | exclude 'META-INF/DEPENDENCIES.txt' 23 | exclude 'META-INF/DEPENDENCIES' 24 | exclude 'META-INF/dependencies.txt' 25 | exclude 'META-INF/LICENSE.txt' 26 | exclude 'META-INF/LICENSE' 27 | exclude 'META-INF/license.txt' 28 | exclude 'META-INF/LGPL2.1' 29 | exclude 'META-INF/NOTICE.txt' 30 | exclude 'META-INF/NOTICE' 31 | exclude 'META-INF/notice.txt' 32 | } 33 | } 34 | 35 | dependencies { 36 | compile fileTree(dir: 'libs', include: ['*.jar']) 37 | compile 'com.fasterxml.jackson.core:jackson-databind:2.7.4' 38 | compile 'com.android.support:appcompat-v7:23.4.0' 39 | compile 'com.squareup.retrofit2:retrofit:2.0.2' 40 | compile 'com.squareup.retrofit2:converter-jackson:2.0.2' 41 | 42 | testCompile 'junit:junit:4.12' 43 | testCompile "org.mockito:mockito-core:1.9.5" 44 | } 45 | -------------------------------------------------------------------------------- /android-sdk/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/connorfoody/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/DreamFactoryApp.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.pm.ApplicationInfo; 6 | import android.content.pm.PackageManager; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | 10 | /** 11 | * Created by Nirmel on 6/3/16. 12 | */ 13 | public class DreamFactoryApp extends Application { 14 | 15 | private static Context context; 16 | 17 | public static String SESSION_TOKEN; 18 | 19 | public static String INSTANCE_URL; 20 | 21 | public static String DB_SVC; 22 | 23 | public static String API_KEY; 24 | 25 | public void onCreate() { 26 | super.onCreate(); 27 | 28 | DreamFactoryApp.context = getApplicationContext(); 29 | 30 | try { 31 | ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); 32 | 33 | Bundle bundle = ai.metaData; 34 | 35 | SESSION_TOKEN= bundle.getString("sessionToken"); 36 | INSTANCE_URL= bundle.getString("instanceUrl"); 37 | DB_SVC= bundle.getString("dbSvc"); 38 | API_KEY= bundle.getString("apiKey"); 39 | } catch (PackageManager.NameNotFoundException e) { 40 | Log.e(DreamFactoryApp.class.getSimpleName(), "Error while reading application meta data", e); 41 | } 42 | } 43 | 44 | public static Context getAppContext() { 45 | return DreamFactoryApp.context; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.util.Log; 7 | 8 | /** 9 | * Created by Nirmel on 6/8/2016. 10 | * 11 | * Base activity for all other activities in application 12 | */ 13 | public abstract class BaseActivity extends Activity { 14 | 15 | public BaseActivity() { 16 | } 17 | 18 | /** 19 | * Log error message 20 | * 21 | * @param message 22 | */ 23 | public void logError(String message) { 24 | Log.e(this.getClass().getSimpleName(), message); 25 | } 26 | 27 | /** 28 | * Log error message with included stacktrace 29 | * 30 | * @param message 31 | * @param t 32 | */ 33 | public void logError(String message, Throwable t) { 34 | Log.e(this.getClass().getSimpleName(), message, t); 35 | } 36 | 37 | /** 38 | * Show and log error message with included stacktrace 39 | * 40 | * @param message 41 | * @param t 42 | */ 43 | public void showError(String message, Throwable t) { 44 | showError(message + " " + (t.getMessage() != null ? t.getMessage() : "")); 45 | 46 | logError(message, t); 47 | } 48 | 49 | /** 50 | * Show error message 51 | * 52 | * @param message 53 | */ 54 | public void showError(String message) { 55 | new AlertDialog.Builder(this) 56 | .setTitle("Error") 57 | .setMessage(message) 58 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 59 | public void onClick(DialogInterface dialog, int which) { 60 | dialog.dismiss(); 61 | } 62 | }) 63 | .setIcon(android.R.drawable.ic_dialog_alert) 64 | .show(); 65 | 66 | logError(message); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/ChooseImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.net.Uri; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.ImageButton; 12 | import android.widget.ListView; 13 | 14 | import com.dreamfactory.sampleapp.R; 15 | import com.dreamfactory.sampleapp.adapters.ProfileImageChooserAdapter; 16 | 17 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 18 | import com.dreamfactory.sampleapp.api.services.ImageService; 19 | import com.dreamfactory.sampleapp.models.ErrorMessage; 20 | import com.dreamfactory.sampleapp.models.FileRecord; 21 | import com.dreamfactory.sampleapp.models.Resource; 22 | import com.dreamfactory.sampleapp.utils.ImageUtil; 23 | 24 | import java.io.File; 25 | 26 | import okhttp3.MediaType; 27 | import okhttp3.RequestBody; 28 | import retrofit2.Call; 29 | import retrofit2.Callback; 30 | import retrofit2.Response; 31 | 32 | /** 33 | * Activity used for displaying profile images 34 | * related to user and choosing one as profile image 35 | * 36 | */ 37 | public class ChooseImageActivity extends BaseActivity { 38 | 39 | protected static final int CHOOSE_IMAGE_REQUEST = 202; 40 | 41 | protected static final int REQUEST_READ_EXTERNAL_STORAGE_PERMISSION = 121; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_choose_image); 47 | 48 | refresh(); 49 | 50 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 51 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 52 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 53 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 54 | final Button uploadPhotoButton = (Button) findViewById(R.id.upload_photo); 55 | 56 | addButton.setVisibility(View.INVISIBLE); 57 | 58 | backButton.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | setResult(RESULT_CANCELED); 62 | finish(); 63 | } 64 | }); 65 | 66 | editButton.setVisibility(View.INVISIBLE); 67 | saveButton.setVisibility(View.INVISIBLE); 68 | 69 | uploadPhotoButton.setOnClickListener(new View.OnClickListener() { 70 | @Override 71 | public void onClick(View v) { 72 | takePicture(); 73 | } 74 | }); 75 | } 76 | 77 | private void takePicture() { 78 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 79 | if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 80 | != PackageManager.PERMISSION_GRANTED) { 81 | 82 | requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 83 | REQUEST_READ_EXTERNAL_STORAGE_PERMISSION); 84 | return; 85 | } 86 | } 87 | 88 | ChooseImageActivity.this.startActivityForResult(Intent.createChooser( 89 | new Intent(Intent.ACTION_GET_CONTENT).setType("image/*"), 90 | "Choose an image"), CHOOSE_IMAGE_REQUEST); 91 | } 92 | 93 | @Override 94 | public void onRequestPermissionsResult(int requestCode, 95 | String permissions[], int[] grantResults) { 96 | switch (requestCode) { 97 | case REQUEST_READ_EXTERNAL_STORAGE_PERMISSION: 98 | if (grantResults.length > 0 99 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 100 | takePicture(); 101 | } 102 | } 103 | } 104 | 105 | private void refresh() { 106 | Intent intent = getIntent(); 107 | 108 | final Long contactId = intent.getLongExtra("contactId", 0); 109 | 110 | if(contactId == 0){ 111 | logError("No contact id sent"); 112 | } else{ 113 | final ImageService service = DreamFactoryAPI.getInstance().getService(ImageService.class); 114 | 115 | service.getProfileImages(contactId).enqueue(new Callback>() { 116 | @Override 117 | public void onResponse(Call> call, 118 | Response> response) { 119 | if(response.isSuccessful()){ 120 | ProfileImageChooserAdapter profileImageChooserAdapter = 121 | new ProfileImageChooserAdapter(ChooseImageActivity.this, 122 | response.body().getResource()); 123 | 124 | ListView listView = (ListView) findViewById(R.id.profile_image_list); 125 | listView.setAdapter(profileImageChooserAdapter); 126 | } else { 127 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 128 | 129 | // Skip folder not found error 130 | if(e.getError().getCode() != 404L) { 131 | onFailure(call, e.toException()); 132 | } 133 | } 134 | } 135 | 136 | @Override 137 | public void onFailure(Call> call, Throwable t) { 138 | showError("Error while loading profile images.", t); 139 | } 140 | }); 141 | } 142 | } 143 | 144 | protected void onActivityResult(int requestCode, int resultCode,Intent imageReturnedIntent) { 145 | super.onActivityResult(requestCode, resultCode, imageReturnedIntent); 146 | switch(requestCode) { 147 | case CHOOSE_IMAGE_REQUEST: 148 | if(resultCode == RESULT_OK){ 149 | Uri uri = imageReturnedIntent.getData(); 150 | 151 | final String profileImagePath = ImageUtil.getImagePath(uri, getContentResolver()); 152 | 153 | final ImageService imageService = DreamFactoryAPI.getInstance().getService(ImageService.class); 154 | 155 | final Long contactId = getIntent().getLongExtra("contactId", 0); 156 | 157 | imageService.addFolder(contactId).enqueue(new Callback() { 158 | @Override 159 | public void onResponse(Call call, Response response) { 160 | if(response.isSuccessful()){ 161 | addProfileImage(contactId, profileImagePath); 162 | } else { 163 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 164 | 165 | if(e.getError().getCode() == 400L) { 166 | addProfileImage(contactId, profileImagePath); 167 | } else { 168 | onFailure(call, e.toException()); 169 | } 170 | } 171 | } 172 | 173 | @Override 174 | public void onFailure(Call call, Throwable t) { 175 | showError("Error while creating contact folder.", t); 176 | } 177 | }); 178 | } 179 | } 180 | } 181 | 182 | private void addProfileImage(Long contactId, String profileImagePath) { 183 | final String imageName = profileImagePath.substring(profileImagePath.lastIndexOf("/") + 1); 184 | 185 | RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), new File(profileImagePath)); 186 | 187 | final ImageService imageService = DreamFactoryAPI.getInstance().getService(ImageService.class); 188 | 189 | imageService.addProfileImage(contactId, imageName, requestBody).enqueue(new Callback() { 190 | @Override 191 | public void onResponse(Call call, Response response) { 192 | if(!response.isSuccessful()) { 193 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 194 | 195 | onFailure(call, e.toException()); 196 | } else { 197 | refresh(); 198 | } 199 | } 200 | 201 | @Override 202 | public void onFailure(Call call, Throwable t) { 203 | showError("Error while uploading image.", t); 204 | } 205 | }); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/ContactListActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.ContextMenu; 6 | import android.view.MenuInflater; 7 | import android.view.View; 8 | import android.widget.AbsListView; 9 | import android.widget.AdapterView; 10 | import android.widget.ImageButton; 11 | import android.widget.ListView; 12 | 13 | import com.dreamfactory.sampleapp.R; 14 | import com.dreamfactory.sampleapp.adapters.ContactListAdapter; 15 | import com.dreamfactory.sampleapp.adapters.DeletableContactListAdapter; 16 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 17 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 18 | import com.dreamfactory.sampleapp.models.ContactRecord; 19 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 20 | 21 | import com.dreamfactory.sampleapp.models.ErrorMessage; 22 | import com.dreamfactory.sampleapp.models.Resource; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import retrofit2.Call; 27 | import retrofit2.Callback; 28 | import retrofit2.Response; 29 | 30 | /** 31 | * Activity responsible for showing contact list 32 | * 33 | */ 34 | public class ContactListActivity extends BaseActivity { 35 | 36 | private Call> getContactsInGroupCall; 37 | 38 | private ListView listView; 39 | private ContactListAdapter contactListAdapter; 40 | 41 | private Long contactGroupId; 42 | private String groupName; 43 | 44 | private final int EDIT_CONTACT_ACTIVITY_REQUEST_CODE = 1; 45 | private final int CREATE_CONTACT_ACTIVITY_REQUEST_CODE = 2; 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_contact_list); 51 | 52 | // get the groupRecordId from the intent 53 | Intent intent = getIntent(); 54 | contactGroupId = intent.getLongExtra("groupRecordId", 0); 55 | groupName = intent.getStringExtra("groupName"); 56 | 57 | loadGroupContacts(contactGroupId); 58 | 59 | listView = (ListView) findViewById(R.id.contactList); 60 | registerForContextMenu(listView); 61 | listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); 62 | 63 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 64 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 65 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 66 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 67 | 68 | addButton.setOnClickListener(new View.OnClickListener() { 69 | @Override 70 | public void onClick(View v) { 71 | Intent intent = new Intent(getBaseContext(), CreateContactActivity.class); 72 | intent.putExtra("contactGroupId", contactGroupId); 73 | 74 | ContactListActivity.this.startActivityForResult(intent, CREATE_CONTACT_ACTIVITY_REQUEST_CODE); 75 | } 76 | }); 77 | 78 | backButton.setOnClickListener(new View.OnClickListener() { 79 | @Override 80 | public void onClick(View v) { 81 | finish(); 82 | } 83 | }); 84 | 85 | editButton.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | Intent intent = new Intent(getBaseContext(), GroupActivity.class); 89 | intent.putExtra("contactGroupId", contactGroupId); 90 | intent.putExtra("groupName", groupName); 91 | 92 | ContactListActivity.this.startActivityForResult(intent, EDIT_CONTACT_ACTIVITY_REQUEST_CODE); 93 | } 94 | }); 95 | 96 | saveButton.setVisibility(View.INVISIBLE); 97 | } 98 | 99 | private void loadGroupContacts(Long contactGroupId) { 100 | if(getContactsInGroupCall != null) { 101 | getContactsInGroupCall.cancel(); 102 | } 103 | 104 | final ContactGroupService service = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 105 | 106 | getContactsInGroupCall = service.getGroupContacts("contact_group_id=" + contactGroupId); 107 | 108 | getContactsInGroupCall.enqueue(new Callback>() { 109 | @Override 110 | public void onResponse(Call> call, Response> response) { 111 | if(response.isSuccessful()){ 112 | List contactRecordRelationships = response.body().getResource(); 113 | 114 | List contactRecords = new ArrayList<>(); 115 | 116 | for(ContactsRelationalRecord rel : contactRecordRelationships) { 117 | contactRecords.add(rel.getContact()); 118 | } 119 | 120 | if(contactListAdapter == null) { 121 | // if there is no adapter, create a new one 122 | contactListAdapter = new ContactListAdapter(ContactListActivity.this, contactRecords); 123 | listView.setAdapter(contactListAdapter); 124 | listView.setMultiChoiceModeListener(new DeletableContactListAdapter(contactListAdapter)); 125 | listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { 126 | @Override 127 | public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { 128 | ((ListView) view).setItemChecked(position, true); 129 | contactListAdapter.set(position, true); 130 | return true; 131 | } 132 | }); 133 | 134 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 135 | @Override 136 | public void onItemClick(AdapterView parent, View view, int position, long id) { 137 | contactListAdapter.handleClick(position); 138 | } 139 | }); 140 | } else{ 141 | // update the adapter data 142 | contactListAdapter.mRecordsList = contactRecords; 143 | contactListAdapter.notifyDataSetChanged(); 144 | } 145 | } else { 146 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 147 | 148 | onFailure(call, e.toException()); 149 | } 150 | } 151 | 152 | @Override 153 | public void onFailure(Call> call, Throwable t) { 154 | if(!call.isCanceled()) { 155 | showError("Error while loading group contacts.", t); 156 | } 157 | } 158 | }); 159 | } 160 | 161 | @Override 162 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 163 | super.onActivityResult(requestCode, resultCode, data); 164 | if(resultCode == RESULT_OK){ 165 | loadGroupContacts(contactGroupId); 166 | } 167 | } 168 | 169 | @Override 170 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { 171 | MenuInflater inflater = getMenuInflater(); 172 | inflater.inflate(R.menu.menu_contact_list, menu); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/ContactViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.os.AsyncTask; 7 | import android.os.Bundle; 8 | import android.os.Parcelable; 9 | import android.util.Base64; 10 | import android.view.View; 11 | import android.widget.ImageButton; 12 | import android.widget.ImageView; 13 | import android.widget.LinearLayout; 14 | import android.widget.TextView; 15 | 16 | import com.dreamfactory.sampleapp.DreamFactoryApp; 17 | import com.dreamfactory.sampleapp.R; 18 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 19 | import com.dreamfactory.sampleapp.api.services.ContactInfoService; 20 | import com.dreamfactory.sampleapp.api.services.ContactService; 21 | import com.dreamfactory.sampleapp.api.services.ImageService; 22 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 23 | import com.dreamfactory.sampleapp.models.ContactRecord; 24 | import com.dreamfactory.sampleapp.models.ErrorMessage; 25 | import com.dreamfactory.sampleapp.models.FileRecord; 26 | 27 | import com.dreamfactory.sampleapp.models.Resource; 28 | import com.dreamfactory.sampleapp.customviews.InfoViewGroup; 29 | 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | import retrofit2.Call; 34 | import retrofit2.Callback; 35 | import retrofit2.Response; 36 | 37 | /** 38 | * Activity responsible for showing contact info 39 | * 40 | */ 41 | public class ContactViewActivity extends BaseActivity { 42 | 43 | private static Integer EDIT_CONTACT_REQUEST = 201; 44 | 45 | private ContactRecord.Parcelable contactRecord; 46 | 47 | private Resource.Parcelable contactInfoRecords; 48 | 49 | private LinearLayout linearLayout; 50 | 51 | private List infoViewGroupList; 52 | 53 | private boolean changedContact; 54 | 55 | @Override 56 | protected void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | setContentView(R.layout.activity_contact_view); 59 | 60 | changedContact = false; 61 | 62 | contactRecord = getIntent().getParcelableExtra("contactRecord"); 63 | 64 | // put the data in the view 65 | buildContactView(); 66 | 67 | linearLayout = (LinearLayout) findViewById(R.id.contactViewTable); 68 | linearLayout.setOrientation(LinearLayout.VERTICAL); 69 | 70 | final ContactInfoService service = DreamFactoryAPI.getInstance().getService(ContactInfoService.class); 71 | 72 | service.getContactInfo("contact_id=" + contactRecord.getId()).enqueue(new Callback>() { 73 | @Override 74 | public void onResponse(Call> call, Response> response) { 75 | if(response.isSuccessful()){ 76 | Resource records = response.body(); 77 | 78 | contactInfoRecords = new Resource.Parcelable<>(); 79 | 80 | for(ContactInfoRecord record : records.getResource()) { 81 | contactInfoRecords.addResource(new ContactInfoRecord.Parcelable(record)); 82 | } 83 | 84 | // build the views once you have the data 85 | buildContactInfoViews(); 86 | } else { 87 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 88 | 89 | onFailure(call, e.toException()); 90 | } 91 | } 92 | 93 | @Override 94 | public void onFailure(Call> call, Throwable t) { 95 | showError("Error while loading contact info.", t); 96 | } 97 | }); 98 | 99 | infoViewGroupList = new ArrayList<>(); 100 | 101 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 102 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 103 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 104 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 105 | 106 | addButton.setVisibility(View.INVISIBLE); 107 | 108 | backButton.setOnClickListener(new View.OnClickListener() { 109 | @Override 110 | public void onClick(View v) { 111 | if (changedContact) { 112 | setResult(RESULT_OK); 113 | } else { 114 | setResult(RESULT_CANCELED); 115 | } 116 | 117 | finish(); 118 | } 119 | }); 120 | 121 | editButton.setOnClickListener(new View.OnClickListener() { 122 | @Override 123 | public void onClick(View v) { 124 | Intent intent = new Intent(getBaseContext(), EditContactActivity.class); 125 | intent.putExtra("contactRecord", (Parcelable) contactRecord); 126 | intent.putExtra("contactInfoRecords", (Parcelable) contactInfoRecords); 127 | 128 | ContactViewActivity.this.startActivityForResult(intent, EDIT_CONTACT_REQUEST); 129 | } 130 | }); 131 | 132 | saveButton.setVisibility(View.INVISIBLE); 133 | 134 | if(!contactRecord.getImageUrl().isEmpty()){ 135 | // only fetch the profile image if the user has one 136 | getProfileImage(); 137 | } 138 | } 139 | 140 | @Override 141 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 142 | super.onActivityResult(requestCode, resultCode, data); 143 | 144 | if(requestCode == EDIT_CONTACT_REQUEST && resultCode == RESULT_OK){ 145 | ContactRecord.Parcelable tmpRecord = data.getParcelableExtra("contactRecord"); 146 | 147 | Resource.Parcelable tmpContactInfoRecords = 148 | data.getParcelableExtra("contactInfoRecords"); 149 | 150 | // refresh the view with the new data 151 | for(InfoViewGroup infoViewGroup : infoViewGroupList){ 152 | infoViewGroup.removeFromParent(); 153 | } 154 | 155 | if(!tmpRecord.equals(contactRecord)){ 156 | // only update the contact view if it changed 157 | contactRecord = tmpRecord; 158 | 159 | final ContactService service = DreamFactoryAPI.getInstance().getService(ContactService.class); 160 | 161 | Resource resource = new Resource<>(); 162 | resource.addResource(contactRecord); 163 | 164 | if(contactRecord.getImageUrl() != null) { 165 | contactRecord.setImageUrl(DreamFactoryApp.INSTANCE_URL + "files/profile_images/" 166 | + contactRecord.getId() + "/" + contactRecord.getImageUrl()); 167 | } 168 | 169 | service.updateContacts(resource).enqueue(new Callback>() { 170 | @Override 171 | public void onResponse(Call> call, Response> response) { 172 | if(response.isSuccessful()){ 173 | changedContact = true; 174 | 175 | if(!contactRecord.getImageUrl().isEmpty()) { 176 | // re-get the contact profile image 177 | getProfileImage(); 178 | } 179 | } else { 180 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 181 | 182 | onFailure(call, e.toException()); 183 | } 184 | } 185 | 186 | @Override 187 | public void onFailure(Call> call, Throwable t) { 188 | showError("Error while updating contact.", t); 189 | } 190 | }); 191 | } 192 | 193 | final Resource resource = new Resource<>(); 194 | 195 | for(int i = 0; i < contactInfoRecords.getResource().size(); i++){ 196 | // contactInfo only grows 197 | if(!tmpContactInfoRecords.getResource().get(i).equals(contactInfoRecords.getResource().get(i))) { 198 | // if any element changed, add it 199 | resource.addResource(tmpContactInfoRecords.getResource().get(i)); 200 | } 201 | } 202 | 203 | if(tmpContactInfoRecords.getResource().size() != contactInfoRecords.getResource().size() 204 | || resource.getResource().size() > 0){ 205 | contactInfoRecords = tmpContactInfoRecords; 206 | infoViewGroupList.clear(); 207 | buildContactView(); 208 | buildContactInfoViews(); 209 | } 210 | 211 | if(resource.getResource().size() > 0) { 212 | final ContactInfoService service = DreamFactoryAPI.getInstance() 213 | .getService(ContactInfoService.class); 214 | 215 | service.updateContactInfos(resource).enqueue(new Callback>() { 216 | @Override 217 | public void onResponse(Call> call, 218 | Response> response) { 219 | if(response.isSuccessful()){ 220 | changedContact = true; 221 | } else { 222 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 223 | 224 | onFailure(call, e.toException()); 225 | } 226 | } 227 | 228 | @Override 229 | public void onFailure(Call> call, Throwable t) { 230 | showError("Error while updating contact info.", t); 231 | } 232 | }); 233 | } 234 | } 235 | } 236 | 237 | private void getProfileImage() { 238 | final ImageService service = DreamFactoryAPI.getInstance().getService(ImageService.class); 239 | 240 | // Skip possible query params 241 | if(contactRecord.getImageUrl().contains("?")) { 242 | contactRecord.setImageUrl(contactRecord.getImageUrl().substring(0, 243 | contactRecord.getImageUrl().indexOf("?"))); 244 | } 245 | 246 | // Resolve image name from url 247 | String imageName = contactRecord.getImageUrl().substring(contactRecord.getImageUrl().lastIndexOf("/") + 1); 248 | 249 | service.getProfileImage(contactRecord.getId(), imageName).enqueue(new Callback() { 250 | @Override 251 | public void onResponse(Call call, Response response) { 252 | if(response.isSuccessful()){ 253 | FileRecord fileRecord = response.body(); 254 | 255 | if(fileRecord.getContent() != null) { 256 | ConvertToBitmap convertToBitmapTask = new ConvertToBitmap(); 257 | convertToBitmapTask.execute(fileRecord.getContent()); 258 | } 259 | } else { 260 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 261 | 262 | onFailure(call, e.toException()); 263 | } 264 | } 265 | 266 | @Override 267 | public void onFailure(Call call, Throwable t) { 268 | showError("Error while loading profile image.", t); 269 | } 270 | }); 271 | } 272 | 273 | private void buildContactView(){ 274 | TextView nameLabel = (TextView) findViewById(R.id.contactName); 275 | nameLabel.setText(contactRecord.getFullName()); 276 | 277 | TextView skypeLabel = (TextView) findViewById(R.id.skypeLabel); 278 | skypeLabel.setText(contactRecord.getSkype()); 279 | 280 | TextView twitterLabel = (TextView) findViewById(R.id.twitterLabel); 281 | twitterLabel.setText(contactRecord.getTwitter()); 282 | 283 | TextView notesLabel = (TextView) findViewById(R.id.notesLabel); 284 | notesLabel.setText(contactRecord.getNotes()); 285 | } 286 | 287 | private void buildContactInfoViews(){ 288 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(linearLayout.getLayoutParams()); 289 | 290 | for(ContactInfoRecord record : contactInfoRecords.getResource()){ 291 | InfoViewGroup infoViewGroup = new InfoViewGroup(ContactViewActivity.this, record); 292 | linearLayout.addView(infoViewGroup, params); 293 | infoViewGroupList.add(infoViewGroup); 294 | } 295 | } 296 | 297 | /** 298 | * Task responsible for converting base64 string to bitmap on separate thread 299 | * 300 | */ 301 | private class ConvertToBitmap extends AsyncTask { 302 | protected Bitmap doInBackground(String... contents) { 303 | byte[] decodedString = Base64.decode(contents[0], Base64.DEFAULT); 304 | 305 | Bitmap bitmap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length); 306 | 307 | return bitmap; 308 | } 309 | 310 | protected void onPostExecute(Bitmap bitmap) { 311 | final ImageView imageView = (ImageView) findViewById(R.id.contact_view_profile_image); 312 | 313 | if(imageView != null) { 314 | imageView.setImageBitmap(bitmap); 315 | } 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/EditContactActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Parcelable; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.ImageButton; 9 | import android.widget.LinearLayout; 10 | 11 | import com.dreamfactory.sampleapp.R; 12 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 13 | import com.dreamfactory.sampleapp.api.services.ContactInfoService; 14 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 15 | import com.dreamfactory.sampleapp.models.ContactRecord; 16 | import com.dreamfactory.sampleapp.models.ErrorMessage; 17 | import com.dreamfactory.sampleapp.models.Resource; 18 | import com.dreamfactory.sampleapp.customviews.EditInfoViewGroup; 19 | 20 | import java.util.ArrayList; 21 | 22 | import retrofit2.Call; 23 | import retrofit2.Callback; 24 | import retrofit2.Response; 25 | 26 | /** 27 | * Activity responsible for editing contact 28 | */ 29 | public class EditContactActivity extends CreateContactActivity { 30 | 31 | private Resource.Parcelable contactInfoRecords; 32 | 33 | private ContactRecord.Parcelable contactRecord; 34 | 35 | private boolean contactInfosCreated = false; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | // CreateContactActivity calls handleIntent, buildViews, handleButtons 40 | super.onCreate(savedInstanceState); 41 | } 42 | 43 | @Override 44 | protected void handleIntent(Intent intent){ 45 | contactRecord = intent.getParcelableExtra("contactRecord"); 46 | contactInfoRecords = intent.getParcelableExtra("contactInfoRecords"); 47 | } 48 | 49 | @Override 50 | protected void buildViews(){ 51 | firstNameEditText.setText(contactRecord.getFirstName()); 52 | lastNameEditText.setText(contactRecord.getLastName()); 53 | twitterEditText.setText(contactRecord.getTwitter()); 54 | skypeEditText.setText(contactRecord.getSkype()); 55 | notesEditText.setText(contactRecord.getNotes()); 56 | 57 | 58 | editInfoViewGroupList = new ArrayList<>(); 59 | for(ContactInfoRecord record : contactInfoRecords.getResource()){ 60 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(linearLayout.getLayoutParams()); 61 | EditInfoViewGroup editInfoViewGroup = new EditInfoViewGroup(this, record); 62 | linearLayout.addView(editInfoViewGroup, params); 63 | editInfoViewGroupList.add(editInfoViewGroup); 64 | } 65 | } 66 | 67 | @Override 68 | protected void handleButtons (){ 69 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 70 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 71 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 72 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 73 | 74 | addButton.setVisibility(View.INVISIBLE); 75 | 76 | backButton.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | setResult(RESULT_CANCELED); 80 | finish(); 81 | } 82 | }); 83 | 84 | editButton.setVisibility(View.INVISIBLE); 85 | 86 | saveButton.setOnClickListener(new View.OnClickListener() { 87 | @Override 88 | public void onClick(View v) { 89 | if (mandatoryFieldsOk()) { // require all contacts to have a first and last name 90 | Intent intent = buildIntent(); 91 | 92 | if(contactInfosCreated) { 93 | setResult(RESULT_OK, intent); 94 | 95 | finish(); 96 | } 97 | } else { 98 | Log.w("editContactActivity", "did not fill in mandatory fields"); 99 | } 100 | } 101 | }); 102 | 103 | chooseImageButton.setOnClickListener(new View.OnClickListener() { 104 | @Override 105 | public void onClick(View v) { 106 | Intent intent = new Intent(getBaseContext(), ChooseImageActivity.class); 107 | intent.putExtra("contactId", contactRecord.getId()); 108 | EditContactActivity.this.startActivityForResult(intent, CHOOSE_IMAGE_REQUEST); 109 | } 110 | }); 111 | } 112 | 113 | @Override 114 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 115 | super.onActivityResult(requestCode, resultCode, data); 116 | if(resultCode == RESULT_OK){ 117 | contactRecord.setImageUrl(data.getStringExtra("imageUrl")); 118 | } 119 | } 120 | 121 | private boolean mandatoryFieldsOk(){ 122 | // returns true if contact has a first and last name, plus every contact_info record has a tittle 123 | if(firstNameEditText.getText().toString().isEmpty() || lastNameEditText.getText().toString().isEmpty()){ 124 | return false; 125 | } 126 | 127 | for(EditInfoViewGroup editInfoViewGroup : editInfoViewGroupList){ 128 | if(!editInfoViewGroup.mandatoryFieldsOk()){ 129 | return false; 130 | } 131 | } 132 | return true; 133 | } 134 | 135 | 136 | private Intent buildIntent(){ 137 | // just to keep onCreate a little cleaner 138 | final Intent intent = new Intent(); 139 | 140 | contactRecord.setFirstName(firstNameEditText.getText().toString()); 141 | contactRecord.setLastName(lastNameEditText.getText().toString()); 142 | contactRecord.setTwitter(twitterEditText.getText().toString()); 143 | contactRecord.setSkype(skypeEditText.getText().toString()); 144 | contactRecord.setNotes(notesEditText.getText().toString()); 145 | 146 | // build the info view 147 | Resource.Parcelable tmpContactInfoRecord = new Resource.Parcelable<>(); 148 | 149 | Resource contactInfoRecords = new Resource<>(); 150 | for(EditInfoViewGroup editInfoViewGroup : editInfoViewGroupList){ 151 | ContactInfoRecord.Parcelable tmp = editInfoViewGroup.buildToContactInfoRecord(); 152 | 153 | if(tmp.getId() == null){ 154 | // new records don't have an id 155 | tmp.setContactId(contactRecord.getId()); 156 | contactInfoRecords.addResource(tmp); 157 | } 158 | 159 | tmpContactInfoRecord.addResource(tmp); 160 | } 161 | 162 | if(contactInfoRecords.getResource().size() > 0) { 163 | // create any new contact info records 164 | final ContactInfoService contactInfoService = DreamFactoryAPI.getInstance() 165 | .getService(ContactInfoService.class); 166 | 167 | contactInfoService.createContactInfos(contactInfoRecords) 168 | .enqueue(new Callback>() { 169 | @Override 170 | public void onResponse(Call> call, 171 | Response> response) { 172 | if(!response.isSuccessful()){ 173 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 174 | 175 | onFailure(call, e.toException()); 176 | } else { 177 | contactInfosCreated = true; 178 | 179 | setResult(RESULT_OK, intent); 180 | finish(); 181 | } 182 | } 183 | 184 | @Override 185 | public void onFailure(Call> call, Throwable t) { 186 | showError("Error while creating contact info.", t); 187 | } 188 | }); 189 | } else { 190 | contactInfosCreated = true; 191 | } 192 | 193 | intent.putExtra("contactInfoRecords", (Parcelable) tmpContactInfoRecord); 194 | intent.putExtra("contactRecord", (Parcelable) contactRecord); 195 | 196 | return intent; 197 | } 198 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/GroupActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.EditText; 9 | import android.widget.ImageButton; 10 | import android.widget.ListView; 11 | 12 | import com.dreamfactory.sampleapp.R; 13 | import com.dreamfactory.sampleapp.adapters.CreateGroupAdapter; 14 | import com.dreamfactory.sampleapp.adapters.EditGroupAdapter; 15 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 16 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 17 | import com.dreamfactory.sampleapp.api.services.ContactService; 18 | import com.dreamfactory.sampleapp.models.ContactRecord; 19 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 20 | import com.dreamfactory.sampleapp.models.ErrorMessage; 21 | import com.dreamfactory.sampleapp.models.GroupRecord; 22 | import com.dreamfactory.sampleapp.models.Resource; 23 | 24 | import java.util.List; 25 | 26 | import retrofit2.Call; 27 | import retrofit2.Callback; 28 | import retrofit2.Response; 29 | 30 | /** 31 | * Activity responsible for managing group 32 | */ 33 | public class GroupActivity extends BaseActivity { 34 | 35 | protected EditText groupName; 36 | 37 | protected CreateGroupAdapter createGroupAdapter; 38 | 39 | protected ListView listView; 40 | 41 | protected boolean editingGroup; 42 | protected GroupRecord groupRecord; 43 | 44 | protected boolean removeContactsFinished = false; 45 | protected boolean assignContactsFinished = false; 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_group); 51 | 52 | groupName = (EditText) findViewById(R.id.group_edit_group_name); 53 | listView = (ListView) findViewById(R.id.group_edit_list); 54 | 55 | Intent intent = getIntent(); 56 | 57 | if(intent.hasExtra("contactGroupId")){ 58 | editingGroup = true; 59 | groupRecord = new GroupRecord(); 60 | groupRecord.setId(intent.getLongExtra("contactGroupId", 0L)); 61 | groupRecord.setName(intent.getStringExtra("groupName")); 62 | groupName.setText(groupRecord.getName()); 63 | } 64 | else{ 65 | editingGroup = false; 66 | } 67 | 68 | handleButtons(); 69 | 70 | final ContactService service = DreamFactoryAPI.getInstance().getService(ContactService.class); 71 | 72 | service.getAllContacts().enqueue(new Callback>() { 73 | @Override 74 | public void onResponse(Call> call, Response> response) { 75 | if(response.isSuccessful()){ 76 | Resource data = response.body(); 77 | 78 | if(editingGroup){ 79 | createGroupAdapter = new EditGroupAdapter(GroupActivity.this, data.getResource(), groupRecord); 80 | listView.setAdapter(createGroupAdapter); 81 | } 82 | else { 83 | createGroupAdapter = new CreateGroupAdapter(GroupActivity.this, data.getResource()); 84 | listView.setAdapter(createGroupAdapter); 85 | } 86 | 87 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 88 | @Override 89 | public void onItemClick(AdapterView parent, View view, int position, long id) { 90 | createGroupAdapter.handleClick(view); 91 | } 92 | }); 93 | } else { 94 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 95 | 96 | onFailure(call, e.toException()); 97 | } 98 | } 99 | 100 | @Override 101 | public void onFailure(Call> call, Throwable t) { 102 | showError("Error while loading contacts.", t); 103 | } 104 | }); 105 | } 106 | 107 | protected void handleButtons(){ 108 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 109 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 110 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 111 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 112 | 113 | addButton.setVisibility(View.INVISIBLE); 114 | 115 | backButton.setOnClickListener(new View.OnClickListener() { 116 | @Override 117 | public void onClick(View v) { 118 | finish(); 119 | } 120 | }); 121 | 122 | editButton.setVisibility(View.INVISIBLE); 123 | 124 | saveButton.setOnClickListener(new View.OnClickListener() { 125 | @Override 126 | public void onClick(View v) { 127 | handleCompletion(); 128 | } 129 | }); 130 | } 131 | 132 | protected void handleCompletion (){ 133 | final ContactGroupService service = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 134 | 135 | if(editingGroup){ 136 | if(!groupName.getText().toString().equals(groupRecord.getName())){ 137 | groupRecord.setName(groupName.getText().toString()); 138 | 139 | Resource resource = new Resource<>(); 140 | resource.addResource(groupRecord); 141 | 142 | service.updateContactGroups(resource).enqueue(new Callback>() { 143 | @Override 144 | public void onResponse(Call> call, Response> response) { 145 | if(response.isSuccessful()) { 146 | setResult(Activity.RESULT_OK); 147 | 148 | // Close if group members are not changed 149 | if(!((EditGroupAdapter) createGroupAdapter).didGroupChange()){ 150 | finish(); 151 | } 152 | } else { 153 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 154 | 155 | onFailure(call, e.toException()); 156 | } 157 | } 158 | 159 | @Override 160 | public void onFailure(Call> call, Throwable t) { 161 | showError("Error while updating contact group name.", t); 162 | } 163 | }); 164 | } else if(!((EditGroupAdapter) createGroupAdapter).didGroupChange()){ 165 | // don't update calling activity 166 | setResult(Activity.RESULT_CANCELED); 167 | 168 | // Close in case group name or members are not changed 169 | finish(); 170 | } 171 | 172 | if(((EditGroupAdapter) createGroupAdapter).didGroupChange()){ 173 | // only update group members if the group changed 174 | assignContactsToGroup(createGroupAdapter.getSelectedContacts()); 175 | 176 | List contactsToRemove = ((EditGroupAdapter)createGroupAdapter).getContactsToRemove(); 177 | 178 | if(contactsToRemove.size() > 0) { 179 | Resource resourcesToRemove = new Resource<>(); 180 | 181 | for (Long contactId : contactsToRemove) { 182 | ContactsRelationalRecord record = new ContactsRelationalRecord(); 183 | record.setContactId(contactId); 184 | record.setContactGroupId(groupRecord.getId()); 185 | 186 | resourcesToRemove.addResource(record); 187 | } 188 | 189 | service.deleteGroupContacts(resourcesToRemove).enqueue(new Callback>() { 190 | @Override 191 | public void onResponse(Call> call, Response> response) { 192 | if (response.isSuccessful()) { 193 | removeContactsFinished = true; 194 | 195 | synchronized (this) { 196 | if (assignContactsFinished && removeContactsFinished) { 197 | setResult(Activity.RESULT_OK); 198 | 199 | finish(); 200 | } 201 | } 202 | } else { 203 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 204 | 205 | onFailure(call, e.toException()); 206 | } 207 | } 208 | 209 | @Override 210 | public void onFailure(Call> call, Throwable t) { 211 | showError("Error while removing contacts from group.", t); 212 | } 213 | }); 214 | } else { 215 | removeContactsFinished = true; 216 | } 217 | } 218 | } else { 219 | final Resource resource = new Resource<>(); 220 | 221 | groupRecord = new GroupRecord(); 222 | groupRecord.setName(groupName.getText().toString()); 223 | 224 | resource.addResource(groupRecord); 225 | 226 | service.createContactGroups(resource).enqueue(new Callback>() { 227 | @Override 228 | public void onResponse(Call> call, Response> response) { 229 | if(response.isSuccessful()){ 230 | GroupRecord resultGroup = response.body().getResource().get(0); 231 | 232 | groupRecord.setId(resultGroup.getId()); 233 | 234 | List contactsToAssign = createGroupAdapter.getSelectedContacts(); 235 | 236 | assignContactsToGroup(contactsToAssign); 237 | 238 | if(assignContactsFinished) { 239 | setResult(Activity.RESULT_OK); 240 | 241 | finish(); 242 | } 243 | } else{ 244 | setResult(Activity.RESULT_CANCELED); 245 | 246 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 247 | 248 | onFailure(call, e.toException()); 249 | 250 | finish(); 251 | } 252 | } 253 | 254 | @Override 255 | public void onFailure(Call> call, Throwable t) { 256 | showError("Error while creating contact group.", t); 257 | } 258 | }); 259 | } 260 | } 261 | 262 | private void assignContactsToGroup(List contactsToAssign) { 263 | if(contactsToAssign.size() == 0) { 264 | assignContactsFinished = true; 265 | return; 266 | } 267 | 268 | final ContactGroupService service = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 269 | 270 | Resource resourcesToCreate = new Resource<>(); 271 | 272 | for(Long contactId : contactsToAssign) { 273 | ContactsRelationalRecord record = new ContactsRelationalRecord(); 274 | record.setContactId(contactId); 275 | record.setContactGroupId(groupRecord.getId()); 276 | 277 | resourcesToCreate.addResource(record); 278 | } 279 | 280 | service.addGroupContacts(resourcesToCreate).enqueue(new Callback>() { 281 | @Override 282 | public void onResponse(Call> call, Response> response) { 283 | if(response.isSuccessful()) { 284 | assignContactsFinished = true; 285 | 286 | synchronized (this) { 287 | if (assignContactsFinished && (removeContactsFinished || !editingGroup)) { 288 | setResult(Activity.RESULT_OK); 289 | 290 | finish(); 291 | } 292 | } 293 | } else { 294 | setResult(Activity.RESULT_CANCELED); 295 | 296 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 297 | 298 | onFailure(call, e.toException()); 299 | } 300 | } 301 | 302 | @Override 303 | public void onFailure(Call> call, Throwable t) { 304 | showError("Error while assigning contacts to contact group.", t); 305 | } 306 | }); 307 | } 308 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/activities/GroupListActivity.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.AbsListView; 7 | import android.widget.AdapterView; 8 | import android.widget.ImageButton; 9 | import android.widget.ListView; 10 | 11 | import com.dreamfactory.sampleapp.R; 12 | import com.dreamfactory.sampleapp.adapters.DeletableGroupListAdapter; 13 | import com.dreamfactory.sampleapp.adapters.GroupListAdapter; 14 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 15 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 16 | import com.dreamfactory.sampleapp.models.ErrorMessage; 17 | import com.dreamfactory.sampleapp.models.GroupRecord; 18 | import com.dreamfactory.sampleapp.models.Resource; 19 | 20 | import retrofit2.Call; 21 | import retrofit2.Callback; 22 | import retrofit2.Response; 23 | 24 | /** 25 | * Activity responsible for showing group list 26 | */ 27 | public class GroupListActivity extends BaseActivity { 28 | 29 | private GroupListAdapter groupListAdapter; 30 | private ListView listView; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | 36 | setContentView(R.layout.activity_group_list); 37 | 38 | listView = (ListView) findViewById(R.id.groupList); 39 | registerForContextMenu(listView); 40 | listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); 41 | 42 | final ImageButton backButton = (ImageButton) findViewById(R.id.persistent_back_button); 43 | final ImageButton editButton = (ImageButton) findViewById(R.id.persistent_edit_button); 44 | final ImageButton saveButton = (ImageButton) findViewById(R.id.persistent_save_button); 45 | final ImageButton addButton = (ImageButton) findViewById(R.id.persistent_add_button); 46 | 47 | addButton.setOnClickListener(new View.OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | Intent intent = new Intent(getBaseContext(), GroupActivity.class); 51 | GroupListActivity.this.startActivity(intent); 52 | } 53 | }); 54 | 55 | backButton.setOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | finish(); 59 | } 60 | }); 61 | 62 | editButton.setVisibility(View.INVISIBLE); 63 | 64 | saveButton.setVisibility(View.INVISIBLE); 65 | } 66 | 67 | @Override 68 | protected void onResume() { 69 | super.onResume(); 70 | 71 | final ContactGroupService service = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 72 | 73 | service.getGroupList().enqueue(new Callback>() { 74 | @Override 75 | public void onResponse(Call> call, Response> response) { 76 | if(response.isSuccessful()) { 77 | if (groupListAdapter == null) { 78 | // if there is no adapter set, create a new one 79 | groupListAdapter = new GroupListAdapter(GroupListActivity.this, response.body().getResource()); 80 | listView.setAdapter(groupListAdapter); 81 | 82 | // configure press + hold to select delete 83 | listView.setMultiChoiceModeListener(new DeletableGroupListAdapter(groupListAdapter)); 84 | listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { 85 | @Override 86 | public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { 87 | ((ListView) view).setItemChecked(position, true); 88 | groupListAdapter.set(position, true); 89 | return true; 90 | } 91 | }); 92 | 93 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 94 | @Override 95 | public void onItemClick(AdapterView parent, View view, int position, long id) { 96 | groupListAdapter.handleClick(position); 97 | } 98 | }); 99 | 100 | } else { 101 | // if an adapter is set, reload its data 102 | groupListAdapter.setRecords(response.body().getResource()); 103 | } 104 | } else { 105 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 106 | 107 | onFailure(call, e.toException()); 108 | } 109 | } 110 | 111 | @Override 112 | public void onFailure(Call> call, Throwable t) { 113 | showError("Error while loading contact groups.", t); 114 | } 115 | }); 116 | } 117 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/ContactListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.content.Intent; 4 | import android.os.Parcelable; 5 | import android.text.TextUtils; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseAdapter; 10 | import android.widget.TextView; 11 | 12 | import com.dreamfactory.sampleapp.R; 13 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 14 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 15 | import com.dreamfactory.sampleapp.api.services.ContactInfoService; 16 | import com.dreamfactory.sampleapp.api.services.ContactService; 17 | import com.dreamfactory.sampleapp.api.services.ImageService; 18 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 19 | import com.dreamfactory.sampleapp.models.ContactRecord; 20 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 21 | import com.dreamfactory.sampleapp.models.ErrorMessage; 22 | import com.dreamfactory.sampleapp.models.FileRecord; 23 | import com.dreamfactory.sampleapp.models.Resource; 24 | import com.dreamfactory.sampleapp.activities.BaseActivity; 25 | import com.dreamfactory.sampleapp.activities.ContactViewActivity; 26 | import java.util.BitSet; 27 | import java.util.Collections; 28 | import java.util.Comparator; 29 | import java.util.List; 30 | import retrofit2.Call; 31 | import retrofit2.Callback; 32 | import retrofit2.Response; 33 | 34 | public class ContactListAdapter extends BaseAdapter { 35 | 36 | protected BaseActivity activity; 37 | public List mRecordsList; 38 | 39 | protected BitSet mainSet; // track where section headers are in the list view 40 | protected BitSet compareSet; // declared up here for reuse 41 | 42 | // for deleting contacts (children don't delete contacts) 43 | private BitSet deleteSet; 44 | 45 | private boolean contactInfosRemoved = false; 46 | private boolean contactRemovedFromGroups = false; 47 | private boolean removeContactsCalled = false; 48 | 49 | public ContactListAdapter(BaseActivity activity, List records){ 50 | this.activity = activity; 51 | this.mRecordsList = records; 52 | mainSet = null; 53 | setupStructures(); 54 | } 55 | 56 | @Override 57 | public void notifyDataSetChanged() { 58 | super.notifyDataSetChanged(); 59 | setupStructures(); 60 | } 61 | 62 | protected void setupStructures() { 63 | // sort it so it is in order properly 64 | Collections.sort(this.mRecordsList, new SortByLastName()); 65 | 66 | // if every one has different last letter, bitset will be 2x size unless num contacts > 27 67 | mainSet = new BitSet(Math.min(this.mRecordsList.size() * 2, this.mRecordsList.size() + 27)); 68 | compareSet = new BitSet(mainSet.size()); 69 | deleteSet = new BitSet(mainSet.size()); 70 | 71 | String previous = ""; 72 | for(int i = 0; i < this.mRecordsList.size(); i++){ 73 | // insert headers at first letter of last name 74 | if(!mRecordsList.get(i).getLastName().substring(0,1).equalsIgnoreCase(previous)){ 75 | // index of header is at index of actual + cardinality of mainset 76 | mainSet.set(i + mainSet.cardinality()); 77 | previous = mRecordsList.get(i).getLastName().substring(0,1).toUpperCase(); 78 | } 79 | } 80 | } 81 | 82 | protected class SortByLastName implements Comparator{ 83 | @Override 84 | public int compare(ContactRecord lhs, ContactRecord rhs) { 85 | 86 | if(lhs.getLastName().equalsIgnoreCase(rhs.getLastName())){ 87 | return lhs.getFirstName().compareTo(rhs.getFirstName()); 88 | } 89 | return lhs.getLastName().compareToIgnoreCase(rhs.getLastName()); 90 | } 91 | } 92 | 93 | @Override 94 | public int getCount() { 95 | return mRecordsList.size() + mainSet.cardinality(); 96 | } 97 | 98 | @Override 99 | public Object getItem(int position) { 100 | // this is not threadsafe 101 | return mRecordsList.get(position - getNumHeaders(position)); 102 | } 103 | 104 | @Override 105 | public long getItemId(int position) { 106 | return 0; 107 | } 108 | 109 | @Override 110 | public View getView(int position, View convertView, ViewGroup parent) { 111 | View rowView = convertView; 112 | int num_headers = getNumHeaders(position); 113 | boolean isHeader = mainSet.get(position); 114 | 115 | if(rowView == null){ 116 | // reuse views 117 | LayoutInflater inflater = activity.getLayoutInflater(); 118 | rowView = inflater.inflate(R.layout.rowlayout, null); 119 | ContactListHolder viewHolder = new ContactListHolder(); 120 | viewHolder.text = (TextView) rowView.findViewById(R.id.row_text_label); 121 | rowView.setTag(viewHolder); 122 | } 123 | 124 | // fill data 125 | ContactListHolder holder = (ContactListHolder) rowView.getTag(); 126 | if(isHeader){ 127 | rowView.setClickable(true); 128 | // set the header as the first char of the last name 129 | ContactRecord record = mRecordsList.get(position - num_headers); 130 | holder.text.setText(record.getLastName().substring(0, 1).toUpperCase()); 131 | holder.text.setBackgroundColor(activity.getResources().getColor(R.color.contact_list_header)); 132 | } else { 133 | rowView.setClickable(false); 134 | ContactRecord record = mRecordsList.get(position - num_headers); 135 | holder.text.setText(record.getFullName()); 136 | holder.text.setBackgroundColor(activity.getResources().getColor(android.R.color.transparent)); 137 | } 138 | 139 | 140 | return rowView; 141 | } 142 | 143 | protected int getNumHeaders(int position){ 144 | // get cardinality of mainset from 0 to position 145 | compareSet.clear(); 146 | compareSet.set(0, position); 147 | compareSet.and(mainSet); 148 | return compareSet.cardinality(); 149 | } 150 | 151 | private class ContactListHolder { 152 | TextView text; 153 | } 154 | 155 | private void showContactView(ContactRecord contactRecord){ 156 | Intent intent = new Intent(activity, ContactViewActivity.class); 157 | intent.putExtra("contactRecord", (Parcelable) new ContactRecord.Parcelable(contactRecord)); 158 | // need to start for result so contact view can tell contact list if contact list 159 | // should reload the contact list 160 | activity.startActivityForResult(intent, 2); 161 | } 162 | 163 | // for contextual action bar access 164 | public void handleClick(int position){ 165 | // this is short click 166 | int realPosition = position - getNumHeaders(position); 167 | showContactView(mRecordsList.get(realPosition)); 168 | } 169 | 170 | public void set(int position, boolean value) { 171 | deleteSet.set(position, value); 172 | } 173 | 174 | public void deselectAll(){ 175 | deleteSet.clear(); 176 | } 177 | 178 | public void removeAllSelected() { 179 | // remove all contacts selected by the delete adapter 180 | 181 | // build it here so it doesn't get rebuilt in all the requests 182 | final Resource contactIdsToRemove = new Resource<>(); 183 | 184 | for(int i = deleteSet.nextSetBit(0); i >= 0; i = deleteSet.nextSetBit(i + 1)){ 185 | contactIdsToRemove.addResource(mRecordsList.get(i-getNumHeaders(i)).getId()); 186 | if(mRecordsList.get(i-getNumHeaders(i)).getImageUrl() != null && 187 | !mRecordsList.get(i-getNumHeaders(i)).getImageUrl().isEmpty()){ 188 | 189 | final ImageService imageService = DreamFactoryAPI.getInstance().getService(ImageService.class); 190 | 191 | imageService.removeFolder(mRecordsList.get(i-getNumHeaders(i)).getId()).enqueue(new Callback() { 192 | @Override 193 | public void onResponse(Call call, Response response) { 194 | if(!response.isSuccessful()) { 195 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 196 | 197 | onFailure(call, e.toException()); 198 | } 199 | } 200 | 201 | @Override 202 | public void onFailure(Call call, Throwable t) { 203 | activity.showError("Error while updating contact.", t); 204 | } 205 | }); 206 | } 207 | } 208 | 209 | if(contactIdsToRemove.getResource().size() == 0) return; 210 | 211 | final ContactGroupService contactGroupService = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 212 | 213 | contactGroupService.deleteContactsFromGroups(contactIdsToRemove).enqueue(new Callback>() { 214 | @Override 215 | public void onResponse(Call> call, Response> response) { 216 | if(response.isSuccessful()){ 217 | contactRemovedFromGroups = true; 218 | 219 | if(contactRemovedFromGroups && contactInfosRemoved) { 220 | removeContacts(contactIdsToRemove); 221 | } 222 | } else { 223 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 224 | 225 | onFailure(call, e.toException()); 226 | } 227 | } 228 | 229 | @Override 230 | public void onFailure(Call> call, Throwable t) { 231 | activity.showError("Error while removing contact from groups.", t); 232 | } 233 | }); 234 | 235 | final ContactInfoService contactInfoService = DreamFactoryAPI.getInstance().getService(ContactInfoService.class); 236 | 237 | contactInfoService.removeContactInfos(contactIdsToRemove).enqueue(new Callback>() { 238 | @Override 239 | public void onResponse(Call> call, Response> response) { 240 | if(response.isSuccessful()) { 241 | contactInfosRemoved = true; 242 | 243 | if(contactRemovedFromGroups && contactInfosRemoved) { 244 | removeContacts(contactIdsToRemove); 245 | } 246 | } else { 247 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 248 | 249 | // These two errors are fine for delete case 250 | if(e.getError().getCode() == 404L || e.getError().getCode() == 400L) { 251 | activity.logError("Error while removing contact infos.", e.toException()); 252 | 253 | contactInfosRemoved = true; 254 | 255 | if(contactRemovedFromGroups && contactInfosRemoved) { 256 | removeContacts(contactIdsToRemove); 257 | } 258 | } else { 259 | onFailure(call, e.toException()); 260 | } 261 | } 262 | } 263 | 264 | @Override 265 | public void onFailure(Call> call, Throwable t) { 266 | activity.showError("Error while removing contact infos.", t); 267 | } 268 | }); 269 | } 270 | 271 | private void removeContacts(Resource contactIdsToRemove) { 272 | if(removeContactsCalled) return; 273 | 274 | removeContactsCalled = true; 275 | 276 | final ContactService contactService = DreamFactoryAPI.getInstance().getService(ContactService.class); 277 | 278 | contactService.removeContacts(TextUtils.join(",", contactIdsToRemove.getResource())).enqueue(new Callback>() { 279 | @Override 280 | public void onResponse(Call> call, Response> response) { 281 | if(response.isSuccessful()){ 282 | int numRemoved = 0; 283 | for(int i = deleteSet.nextSetBit(0); i >= 0; i = deleteSet.nextSetBit(i + 1)) { 284 | // calculate the new position of object following prev deletes 285 | int realPosition = i - getNumHeaders(i) - numRemoved; 286 | mRecordsList.remove(realPosition); 287 | numRemoved++; 288 | } 289 | // once everything gets through successfully, reload the input views 290 | notifyDataSetChanged(); 291 | } else { 292 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 293 | 294 | onFailure(call, e.toException()); 295 | } 296 | } 297 | 298 | @Override 299 | public void onFailure(Call> call, Throwable t) { 300 | activity.showError("Error while removing contacts.", t); 301 | } 302 | }); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/CreateGroupAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.CheckBox; 7 | import android.widget.TextView; 8 | 9 | import com.dreamfactory.sampleapp.R; 10 | import com.dreamfactory.sampleapp.models.ContactRecord; 11 | import com.dreamfactory.sampleapp.activities.BaseActivity; 12 | 13 | import java.util.ArrayList; 14 | import java.util.BitSet; 15 | import java.util.List; 16 | 17 | public class CreateGroupAdapter extends ContactListAdapter { 18 | protected BitSet selectedSet; 19 | 20 | public CreateGroupAdapter(BaseActivity activity, List records) { 21 | super(activity, records); 22 | 23 | // store selectedSet contacts 24 | selectedSet = new BitSet(records.size()); 25 | 26 | } 27 | @Override 28 | public View getView(int position, View convertView, ViewGroup parent) { 29 | View rowView = convertView; 30 | int numHeaders = getNumHeaders(position); 31 | boolean isHeader = mainSet.get(position); 32 | 33 | if(rowView == null){ 34 | LayoutInflater inflater = activity.getLayoutInflater(); 35 | rowView = inflater.inflate(R.layout.selcetable_row_layout, null); 36 | GroupHolder holder = new GroupHolder(); 37 | holder.textView = (TextView) rowView.findViewById(R.id.selectable_row_label); 38 | holder.checkBox = (CheckBox) rowView.findViewById(R.id.selectable_row_checkbox); 39 | rowView.setTag(holder); 40 | } 41 | 42 | GroupHolder holder = (GroupHolder) rowView.getTag(); 43 | ContactRecord record = mRecordsList.get(position - numHeaders); 44 | 45 | if(isHeader){ 46 | rowView.setClickable(true); 47 | holder.checkBox.setVisibility(View.GONE); 48 | holder.textView.setText(("" + record.getLastName().charAt(0)).toUpperCase()); 49 | holder.textView.setBackgroundColor(activity.getResources().getColor(R.color.contact_list_header)); 50 | } 51 | else{ 52 | rowView.setClickable(false); 53 | holder.textView.setText(record.getFullName()); 54 | 55 | holder.record = record; 56 | 57 | holder.position = position - numHeaders; 58 | 59 | holder.checkBox.setVisibility(View.VISIBLE); 60 | holder.checkBox.setChecked(selectedSet.get(position - numHeaders)); 61 | 62 | holder.textView.setBackgroundColor(activity.getResources().getColor(android.R.color.transparent)); 63 | } 64 | return rowView; 65 | } 66 | 67 | public void handleClick(View v){ 68 | // used by multi modal 69 | GroupHolder groupHolder = (GroupHolder) v.getTag(); 70 | selectedSet.flip(groupHolder.position); 71 | groupHolder.checkBox.setChecked(selectedSet.get(groupHolder.position)); 72 | } 73 | 74 | public List getSelectedContacts() { 75 | // returns list of contacts selected to be in group 76 | List selectedContacts = new ArrayList<>(); 77 | 78 | for(int i = selectedSet.nextSetBit(0); i >= 0; i = selectedSet.nextSetBit(i + 1)){ 79 | selectedContacts.add(mRecordsList.get(i).getId()); 80 | } 81 | return selectedContacts; 82 | } 83 | 84 | protected class GroupHolder { 85 | public ContactRecord record; 86 | 87 | public TextView textView; 88 | public CheckBox checkBox; 89 | 90 | public int position; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/DeletableContactListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.view.ActionMode; 4 | import android.view.Menu; 5 | import android.view.MenuInflater; 6 | import android.view.MenuItem; 7 | import android.widget.AbsListView; 8 | 9 | import com.dreamfactory.sampleapp.R; 10 | 11 | public class DeletableContactListAdapter implements AbsListView.MultiChoiceModeListener { 12 | private ContactListAdapter adapter; 13 | 14 | public DeletableContactListAdapter(ContactListAdapter adapter) { 15 | super(); 16 | this.adapter = adapter; 17 | } 18 | 19 | @Override 20 | public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { 21 | // set if the item is checked or not 22 | adapter.set(position, checked); 23 | } 24 | 25 | @Override 26 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 27 | MenuInflater inflater = mode.getMenuInflater(); 28 | inflater.inflate(R.menu.menu_contact_list, menu); 29 | adapter.deselectAll(); 30 | return true; 31 | } 32 | 33 | @Override 34 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 35 | return false; 36 | } 37 | 38 | @Override 39 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 40 | adapter.removeAllSelected(); 41 | mode.finish(); 42 | return true; 43 | } 44 | 45 | @Override 46 | public void onDestroyActionMode(ActionMode mode) { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/DeletableGroupListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.view.ActionMode; 4 | import android.view.Menu; 5 | import android.view.MenuInflater; 6 | import android.view.MenuItem; 7 | import android.widget.AbsListView; 8 | 9 | import com.dreamfactory.sampleapp.R; 10 | 11 | public class DeletableGroupListAdapter implements AbsListView.MultiChoiceModeListener{ 12 | 13 | private GroupListAdapter adapter; 14 | 15 | public DeletableGroupListAdapter(GroupListAdapter adapter){ 16 | this.adapter = adapter; 17 | } 18 | 19 | @Override 20 | public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { 21 | adapter.set(position, checked); 22 | } 23 | 24 | @Override 25 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 26 | MenuInflater inflater = mode.getMenuInflater(); 27 | inflater.inflate(R.menu.menu_group_list, menu); 28 | adapter.deselectAll(); 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 34 | return false; 35 | } 36 | 37 | @Override 38 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 39 | adapter.removeAllSelected(); 40 | mode.finish(); 41 | return true; 42 | } 43 | 44 | @Override 45 | public void onDestroyActionMode(ActionMode mode) { 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/EditGroupAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 4 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 5 | import com.dreamfactory.sampleapp.models.ContactRecord; 6 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 7 | import com.dreamfactory.sampleapp.models.ErrorMessage; 8 | import com.dreamfactory.sampleapp.models.GroupRecord; 9 | 10 | import com.dreamfactory.sampleapp.models.Resource; 11 | import com.dreamfactory.sampleapp.activities.BaseActivity; 12 | 13 | 14 | import java.util.ArrayList; 15 | import java.util.BitSet; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import retrofit2.Call; 19 | import retrofit2.Callback; 20 | import retrofit2.Response; 21 | 22 | public class EditGroupAdapter extends CreateGroupAdapter { 23 | 24 | protected GroupRecord record; 25 | 26 | protected BitSet inGroupSet; 27 | 28 | public EditGroupAdapter(final BaseActivity activity, List records, GroupRecord record) { 29 | super(activity, records); 30 | 31 | this.record = record; 32 | 33 | inGroupSet = new BitSet(selectedSet.size()); 34 | 35 | final ContactGroupService contactGroupService = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 36 | 37 | contactGroupService.getGroupContacts("contact_group_id=" + record.getId()).enqueue(new Callback>() { 38 | @Override 39 | public void onResponse(Call> call, Response> response) { 40 | if(response.isSuccessful()) { 41 | List contactRecords = new ArrayList<>(); 42 | for(ContactsRelationalRecord record : response.body().getResource()){ 43 | contactRecords.add(record.getContact()); 44 | } 45 | 46 | // sort so we can find these guys in the big contacts list in ~ linear time 47 | Collections.sort(contactRecords, new SortByLastName()); 48 | 49 | int j = 0; 50 | for(int i = 0; i < mRecordsList.size() && j < contactRecords.size(); i++){ 51 | if(mRecordsList.get(i).getId() == contactRecords.get(j).getId()){ 52 | // mark the contacts already in the group 53 | // use inGroupSet so we can tell how things changed later 54 | inGroupSet.set(i); 55 | j++; 56 | } 57 | } 58 | 59 | selectedSet.or(inGroupSet); 60 | notifyDataSetChanged(); 61 | } else { 62 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 63 | 64 | onFailure(call, e.toException()); 65 | } 66 | } 67 | 68 | @Override 69 | public void onFailure(Call> call, Throwable t) { 70 | activity.showError("Error while updating contact info.", t); 71 | } 72 | }); 73 | } 74 | 75 | @Override 76 | public List getSelectedContacts() { 77 | // deselect the contacts already in group first 78 | BitSet tmp = selectedSet.get(0, selectedSet.size()); 79 | selectedSet.andNot(inGroupSet); 80 | // remove the selected contacts from the in group set 81 | inGroupSet.andNot(tmp); 82 | 83 | return super.getSelectedContacts(); 84 | } 85 | 86 | public List getContactsToRemove() { 87 | // called by groupActivity to delete contacts 88 | selectedSet = inGroupSet; 89 | return super.getSelectedContacts(); 90 | } 91 | 92 | public boolean didGroupChange(){ 93 | // checked if the group members changed 94 | if(inGroupSet.cardinality() != selectedSet.cardinality()){ 95 | return true; 96 | } 97 | compareSet.clear(); 98 | compareSet.or(inGroupSet); 99 | compareSet.and(selectedSet); 100 | 101 | // true if a contact is in the group set but not selected 102 | return compareSet.cardinality() != inGroupSet.cardinality(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/GroupListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.content.Intent; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.TextView; 10 | 11 | import com.dreamfactory.sampleapp.R; 12 | import com.dreamfactory.sampleapp.api.DreamFactoryAPI; 13 | import com.dreamfactory.sampleapp.api.services.ContactGroupService; 14 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 15 | import com.dreamfactory.sampleapp.models.ErrorMessage; 16 | import com.dreamfactory.sampleapp.models.GroupRecord; 17 | import com.dreamfactory.sampleapp.models.Resource; 18 | import com.dreamfactory.sampleapp.activities.BaseActivity; 19 | import com.dreamfactory.sampleapp.activities.ContactListActivity; 20 | import java.util.ArrayList; 21 | import java.util.BitSet; 22 | import java.util.List; 23 | 24 | import retrofit2.Call; 25 | import retrofit2.Callback; 26 | import retrofit2.Response; 27 | 28 | public class GroupListAdapter extends BaseAdapter { 29 | 30 | private BaseActivity activity; 31 | private List records; 32 | private BitSet deleteSet; 33 | 34 | public GroupListAdapter(BaseActivity activity, List records){ 35 | this.activity = activity; 36 | this.records = records; 37 | deleteSet = new BitSet(records.size()); 38 | } 39 | 40 | @Override 41 | public int getCount() { 42 | return records.size(); 43 | } 44 | 45 | @Override 46 | public Object getItem(int position) { 47 | return records.get(position); 48 | } 49 | 50 | @Override 51 | public long getItemId(int position) { 52 | return 0; 53 | } 54 | 55 | public void showContactList(Long groupId, String groupName) { 56 | Intent intent = new Intent(activity, ContactListActivity.class); 57 | intent.putExtra("groupRecordId", groupId); 58 | intent.putExtra("groupName", groupName); 59 | activity.startActivity(intent); 60 | } 61 | @Override 62 | public View getView(int position, View convertView, ViewGroup parent) { 63 | View rowView = convertView; 64 | 65 | if(rowView == null){ 66 | // reuse views 67 | LayoutInflater inflater = activity.getLayoutInflater(); 68 | rowView = inflater.inflate(R.layout.rowlayout, null); 69 | GroupListHolder viewHolder = new GroupListHolder(); 70 | viewHolder.text = (TextView) rowView.findViewById(R.id.row_text_label); 71 | rowView.setTag(viewHolder); 72 | } 73 | 74 | rowView.setClickable(false); 75 | // fill data 76 | GroupListHolder holder = (GroupListHolder) rowView.getTag(); 77 | GroupRecord record = records.get(position); 78 | holder.text.setText(record.getName()); 79 | holder.record = record; 80 | 81 | return rowView; 82 | } 83 | 84 | private class GroupListHolder { 85 | TextView text; 86 | GroupRecord record; 87 | } 88 | 89 | public void handleClick(int position){ 90 | GroupRecord record = (GroupRecord) getItem(position); 91 | showContactList(record.getId(), record.getName()); 92 | } 93 | 94 | public void set(int position, boolean value){ 95 | deleteSet.set(position, value); 96 | } 97 | 98 | public void deselectAll(){ 99 | deleteSet.clear(); 100 | } 101 | 102 | public void removeAllSelected() { 103 | // need to delete records with references to the contact_group record before 104 | // deleting the contact group record its self 105 | final ContactGroupService contactGroupService = DreamFactoryAPI.getInstance().getService(ContactGroupService.class); 106 | 107 | // delete multiple records by chaining filters together 108 | StringBuilder builder = new StringBuilder(); 109 | 110 | final List groupIds = new ArrayList<>(); 111 | 112 | int i = deleteSet.nextSetBit(0); 113 | 114 | builder.append("(contact_group_id=").append(records.get(i).getId()).append(")"); 115 | groupIds.add(records.get(i).getId()); 116 | 117 | for(i = deleteSet.nextSetBit(i+1);i >=0; i = deleteSet.nextSetBit(i + 1)){ 118 | builder.append(" and (contact_group_id=").append(records.get(i).getId()).append(")"); 119 | 120 | groupIds.add(records.get(i).getId()); 121 | } 122 | 123 | contactGroupService.deleteContactsFromGroups(builder.toString()).enqueue(new Callback>() { 124 | @Override 125 | public void onResponse(Call> call, Response> response) { 126 | if(response.isSuccessful()){ 127 | contactGroupService.removeGroups(TextUtils.join(",", groupIds)).enqueue(new Callback>() { 128 | @Override 129 | public void onResponse(Call> call, Response> response) { 130 | if(response.isSuccessful()) { 131 | removeFromList(); 132 | } else { 133 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 134 | 135 | onFailure(call, e.toException()); 136 | } 137 | } 138 | 139 | @Override 140 | public void onFailure(Call> call, Throwable t) { 141 | activity.showError("Error while removing contact groups.", t); 142 | } 143 | }); 144 | } else { 145 | ErrorMessage e = DreamFactoryAPI.getErrorMessage(response); 146 | 147 | onFailure(call, e.toException()); 148 | } 149 | } 150 | 151 | @Override 152 | public void onFailure(Call> call, Throwable t) { 153 | activity.showError("Error while removing contact from groups.", t); 154 | } 155 | }); 156 | } 157 | 158 | private void removeFromList() { 159 | // remove selected groups from the list, called once the delete has been OK'd with the server 160 | int deleteOffset = 0; // account for shift that happens as items are deleted from the list 161 | for(int i = deleteSet.nextSetBit(0); i >= 0; i = deleteSet.nextSetBit(i + 1)) { 162 | records.remove(i - deleteOffset); 163 | deleteOffset++; 164 | } 165 | notifyDataSetChanged(); 166 | } 167 | 168 | public void setRecords(List records) { 169 | this.records = records; 170 | 171 | notifyDataSetChanged(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/adapters/ProfileImageChooserAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.adapters; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.TextView; 10 | 11 | import com.dreamfactory.sampleapp.R; 12 | import com.dreamfactory.sampleapp.models.FileRecord; 13 | 14 | import java.util.List; 15 | 16 | public class ProfileImageChooserAdapter extends BaseAdapter { 17 | private Activity context; 18 | private List imageList; 19 | 20 | public ProfileImageChooserAdapter(Activity context, List imageList){ 21 | this.context = context; 22 | this.imageList = imageList; 23 | } 24 | 25 | @Override 26 | public int getCount() { 27 | return imageList.size(); 28 | } 29 | 30 | @Override 31 | public Object getItem(int position) { 32 | return imageList.get(position); 33 | } 34 | 35 | @Override 36 | public long getItemId(int position) { 37 | return 0; 38 | } 39 | 40 | @Override 41 | public View getView(int position, View convertView, ViewGroup parent) { 42 | View rowView = convertView; 43 | 44 | if(rowView == null){ 45 | // reuse views 46 | LayoutInflater inflater = context.getLayoutInflater(); 47 | rowView = inflater.inflate(R.layout.rowlayout, null); 48 | FileHolder viewHolder = new FileHolder(); 49 | viewHolder.text = (TextView) rowView.findViewById(R.id.row_text_label); 50 | rowView.setTag(viewHolder); 51 | } 52 | 53 | rowView.setClickable(false); 54 | // fill data 55 | FileHolder holder = (FileHolder) rowView.getTag(); 56 | String fileName = imageList.get(position).getName(); 57 | holder.text.setText(fileName); 58 | holder.fileName = fileName; 59 | rowView.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View v) { 62 | FileHolder fileHolder = (FileHolder) v.getTag(); 63 | Intent intent = new Intent(); 64 | intent.putExtra("imageUrl", fileHolder.fileName); 65 | context.setResult(Activity.RESULT_OK, intent); 66 | context.finish(); 67 | context = null; 68 | } 69 | }); 70 | return rowView; 71 | } 72 | 73 | private class FileHolder{ 74 | public String fileName; 75 | public TextView text; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/DreamFactoryAPI.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api; 2 | 3 | import android.util.Log; 4 | 5 | import com.dreamfactory.sampleapp.DreamFactoryApp; 6 | import com.dreamfactory.sampleapp.models.ErrorMessage; 7 | import com.dreamfactory.sampleapp.utils.PrefUtil; 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | import com.fasterxml.jackson.databind.DeserializationFeature; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | 12 | import java.io.IOException; 13 | import java.lang.annotation.Annotation; 14 | 15 | import okhttp3.Interceptor; 16 | import okhttp3.OkHttpClient; 17 | import okhttp3.Request; 18 | import okhttp3.ResponseBody; 19 | import retrofit2.Converter; 20 | import retrofit2.Response; 21 | import retrofit2.Retrofit; 22 | import retrofit2.converter.jackson.JacksonConverterFactory; 23 | 24 | /** 25 | * Created by Nirmel on 6/3/2016. 26 | * 27 | * Componenet responsibe for handling services and api calls 28 | */ 29 | public class DreamFactoryAPI { 30 | 31 | private static DreamFactoryAPI INSTANCE; 32 | 33 | private Retrofit retrofit; 34 | 35 | private OkHttpClient httpClient; 36 | 37 | private static Converter errorConverter; 38 | 39 | public static String testToken; 40 | 41 | public static Boolean runningFromTest = false; 42 | 43 | private DreamFactoryAPI() { 44 | httpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() { 45 | @Override 46 | public okhttp3.Response intercept(Chain chain) throws IOException { 47 | Request.Builder ongoing = chain.request().newBuilder(); 48 | 49 | if(DreamFactoryApp.API_KEY == null) { 50 | Log.w(DreamFactoryAPI.class.getSimpleName(), "API key not provided"); 51 | } else { 52 | ongoing.addHeader("X-DreamFactory-Api-Key", DreamFactoryApp.API_KEY); 53 | } 54 | 55 | if(!runningFromTest) { 56 | String token = PrefUtil.getString(DreamFactoryApp.getAppContext(), DreamFactoryApp.SESSION_TOKEN); 57 | 58 | if (token != null && !token.isEmpty()) { 59 | ongoing.addHeader("X-DreamFactory-Session-Token", token); 60 | } 61 | } else if(testToken != null){ 62 | ongoing.addHeader("X-DreamFactory-Session-Token", testToken); 63 | } 64 | 65 | return chain.proceed(ongoing.build()); 66 | } 67 | }).build(); 68 | 69 | ObjectMapper objectMapper = new ObjectMapper(); 70 | objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 71 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 72 | 73 | retrofit = new Retrofit.Builder() 74 | .baseUrl(DreamFactoryApp.INSTANCE_URL) 75 | .client(httpClient) 76 | .addConverterFactory(JacksonConverterFactory.create(objectMapper)) 77 | .build(); 78 | 79 | errorConverter = retrofit.responseBodyConverter(ErrorMessage.class, new Annotation[0]); 80 | } 81 | 82 | public static ErrorMessage getErrorMessage(Response response) { 83 | ErrorMessage error = null; 84 | 85 | try { 86 | error = errorConverter.convert(response.errorBody()); 87 | } catch (IOException e) { 88 | error = new ErrorMessage("Unexpected error"); 89 | 90 | Log.e("ERROR", "Unexpected error while serialising error message", e); 91 | } 92 | 93 | return error; 94 | } 95 | 96 | public synchronized static DreamFactoryAPI getInstance() { 97 | if(INSTANCE == null) { 98 | INSTANCE = new DreamFactoryAPI(); 99 | } 100 | 101 | return INSTANCE; 102 | } 103 | 104 | public T getService(Class serviceClass) { 105 | return retrofit.create(serviceClass); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/services/AuthService.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api.services; 2 | 3 | import com.dreamfactory.sampleapp.models.RegisterResponse; 4 | import com.dreamfactory.sampleapp.models.User; 5 | import com.dreamfactory.sampleapp.models.requests.LoginRequest; 6 | import com.dreamfactory.sampleapp.models.requests.RegisterRequest; 7 | 8 | import retrofit2.Call; 9 | import retrofit2.http.Body; 10 | import retrofit2.http.POST; 11 | import retrofit2.http.Query; 12 | 13 | /** 14 | * Created by Nirmel on 6/3/2016. 15 | */ 16 | public interface AuthService { 17 | 18 | @POST("user/session") 19 | Call userLogin(@Body LoginRequest request); 20 | 21 | @POST("user/register") 22 | Call userRegister(@Body RegisterRequest request, @Query("login") Long login); 23 | } 24 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/services/ContactGroupService.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api.services; 2 | 3 | import com.dreamfactory.sampleapp.models.ContactsRelationalRecord; 4 | import com.dreamfactory.sampleapp.models.GroupRecord; 5 | import com.dreamfactory.sampleapp.models.Resource; 6 | 7 | 8 | import retrofit2.Call; 9 | import retrofit2.http.Body; 10 | import retrofit2.http.DELETE; 11 | import retrofit2.http.GET; 12 | import retrofit2.http.HTTP; 13 | import retrofit2.http.PATCH; 14 | import retrofit2.http.POST; 15 | import retrofit2.http.Path; 16 | import retrofit2.http.Query; 17 | 18 | /** 19 | * Created by Nirmel on 6/3/2016. 20 | */ 21 | public interface ContactGroupService { 22 | 23 | @GET("db/_table/contact_group/{id}") 24 | Call getContactGroup(@Path(value = "id") Long contactGroupId); 25 | 26 | @GET("db/_table/contact_group") 27 | Call> getGroupList(); 28 | 29 | @GET("db/_table/contact_group_relationship?related=contact_by_contact_id") 30 | Call> getGroupContacts(@Query(value = "filter") String filter); 31 | 32 | @HTTP(method = "DELETE", path = "db/_table/contact_group_relationship?id_field=contact_group_id,contact_id", hasBody = true) 33 | Call> deleteGroupContacts(@Body Resource records); 34 | 35 | @HTTP(method = "DELETE", path = "db/_table/contact_group_relationship?id_field=contact_id", hasBody = true) 36 | Call> deleteContactsFromGroups(@Body Resource records); 37 | 38 | @DELETE("db/_table/contact_group_relationship") 39 | Call> deleteContactsFromGroups(@Query(value = "filter") String filter); 40 | 41 | @DELETE("db/_table/contact_group") 42 | Call> removeGroups(@Query(value = "ids") String ids); 43 | 44 | @POST("db/_table/contact_group_relationship") 45 | Call> addGroupContacts(@Body Resource records); 46 | 47 | @POST("db/_table/contact_group") 48 | Call> createContactGroups(@Body Resource records); 49 | 50 | @PATCH("db/_table/contact_group") 51 | Call> updateContactGroups(@Body Resource records); 52 | } 53 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/services/ContactInfoService.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api.services; 2 | 3 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 4 | import com.dreamfactory.sampleapp.models.Resource; 5 | 6 | import retrofit2.Call; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.GET; 9 | import retrofit2.http.HTTP; 10 | import retrofit2.http.PATCH; 11 | import retrofit2.http.POST; 12 | import retrofit2.http.Path; 13 | import retrofit2.http.Query; 14 | 15 | /** 16 | * Created by Nirmel on 6/3/2016. 17 | */ 18 | public interface ContactInfoService { 19 | 20 | @GET("db/_table/contact_info/{id}") 21 | Call getContactInfo(@Path(value = "id") Long contactInfoId); 22 | 23 | @GET("db/_table/contact_info") 24 | Call> getContactInfo(@Query(value = "filter") String filter); 25 | 26 | @POST("db/_table/contact_info") 27 | Call> createContactInfos(@Body Resource records); 28 | 29 | @HTTP(method = "DELETE", path = "db/_table/contact_info?id_field=contact_id&continue=1", hasBody = true) 30 | Call> removeContactInfos(@Body Resource records); 31 | 32 | @PATCH("db/_table/contact_info") 33 | Call> updateContactInfos(@Body Resource records); 34 | } 35 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/services/ContactService.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api.services; 2 | 3 | import com.dreamfactory.sampleapp.models.ContactRecord; 4 | import com.dreamfactory.sampleapp.models.Resource; 5 | 6 | import retrofit2.Call; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.DELETE; 9 | import retrofit2.http.GET; 10 | import retrofit2.http.PATCH; 11 | import retrofit2.http.POST; 12 | import retrofit2.http.Path; 13 | import retrofit2.http.Query; 14 | 15 | /** 16 | * Created by Nirmel on 6/3/2016. 17 | */ 18 | public interface ContactService { 19 | 20 | @GET("db/_table/contact/{id}") 21 | Call getContact(@Path(value = "id") Long contactId); 22 | 23 | @GET("db/_table/contact") 24 | Call> getAllContacts(); 25 | 26 | @POST("db/_table/contact") 27 | Call> createContacts(@Body Resource records); 28 | 29 | @DELETE("db/_table/contact") 30 | Call> removeContacts(@Query(value = "ids") String ids); 31 | 32 | @PATCH("db/_table/contact") 33 | Call> updateContacts(@Body Resource contactRecord); 34 | } 35 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/api/services/ImageService.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.api.services; 2 | 3 | import com.dreamfactory.sampleapp.models.FileRecord; 4 | import com.dreamfactory.sampleapp.models.Resource; 5 | 6 | import okhttp3.RequestBody; 7 | import retrofit2.Call; 8 | import retrofit2.http.Body; 9 | import retrofit2.http.DELETE; 10 | import retrofit2.http.GET; 11 | import retrofit2.http.POST; 12 | import retrofit2.http.Path; 13 | 14 | /** 15 | * Created by Nirmel on 6/3/2016. 16 | */ 17 | public interface ImageService { 18 | 19 | @GET("files/profile_images/{id}/?include_folders=0&include_files=1") 20 | Call> getProfileImages(@Path(value = "id") Long contactId); 21 | 22 | @GET("files/profile_images/{id}/{name}?include_properties=1&content=1&download=1") 23 | Call getProfileImage(@Path(value = "id") Long contactId, @Path(value = "name") String name); 24 | 25 | @POST("files/profile_images/{id}/{name}") 26 | Call addProfileImage(@Path(value = "id") Long contactId, @Path(value = "name") String name, @Body RequestBody file); 27 | 28 | @POST("files/profile_images/{id}/") 29 | Call addFolder(@Path(value = "id") Long contactId); 30 | 31 | @DELETE("files/profile_images/{id}/?force=1") 32 | Call removeFolder(@Path(value = "id") Long contactId); 33 | } 34 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/customviews/EditInfoViewGroup.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.customviews; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.widget.EditText; 6 | import android.widget.LinearLayout; 7 | 8 | import com.dreamfactory.sampleapp.R; 9 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 10 | 11 | public class EditInfoViewGroup extends LinearLayout { 12 | 13 | private EditText type; 14 | private EditText email; 15 | private EditText phone; 16 | private EditText address; 17 | private EditText city; 18 | 19 | private ContactInfoRecord contactInfoRecord; 20 | 21 | public EditInfoViewGroup(Context context, ContactInfoRecord record) { 22 | super(context); 23 | 24 | contactInfoRecord = record; 25 | 26 | LayoutInflater inflater = LayoutInflater.from(context); 27 | inflater.inflate(R.layout.edit_contact_info_layout, this, true); 28 | 29 | type = (EditText) findViewById(R.id.edit_contact_info_type); 30 | email = (EditText) findViewById(R.id.edit_contact_info_email); 31 | phone = (EditText) findViewById(R.id.edit_contact_info_phone); 32 | address = (EditText) findViewById(R.id.edit_contact_info_address); 33 | city = (EditText) findViewById(R.id.edit_contact_info_city); 34 | 35 | if(record == null){ 36 | return; 37 | } 38 | 39 | if(!record.getInfoType().isEmpty()){ 40 | type.setText(record.getInfoType()); 41 | } 42 | 43 | if(!record.getEmail().isEmpty()){ 44 | email.setText(record.getEmail()); 45 | } 46 | 47 | if(!record.getPhone().isEmpty()){ 48 | phone.setText(record.getPhone()); 49 | } 50 | 51 | if(!record.getAddress().isEmpty() && !record.getCity().isEmpty()){ 52 | address.setText(record.getAddress()); 53 | city.setText(record.getCity()); 54 | } 55 | } 56 | 57 | public boolean mandatoryFieldsOk() { 58 | boolean valid = !type.getText().toString().isEmpty(); 59 | 60 | if(!valid) { 61 | type.setError(getResources().getString(R.string.error_field_required)); 62 | } 63 | 64 | return valid; 65 | } 66 | 67 | public ContactInfoRecord.Parcelable buildToContactInfoRecord() { 68 | 69 | // build record and send it back up 70 | 71 | ContactInfoRecord.Parcelable record = new ContactInfoRecord.Parcelable(); 72 | 73 | record.setInfoType(type.getText().toString()); 74 | record.setEmail(email.getText().toString()); 75 | record.setPhone(phone.getText().toString()); 76 | record.setAddress(address.getText().toString()); 77 | record.setCity(city.getText().toString()); 78 | 79 | if (contactInfoRecord != null) { 80 | record.setId(contactInfoRecord.getId()); 81 | record.setContactId(contactInfoRecord.getContactId()); 82 | } 83 | 84 | return record; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/customviews/InfoViewGroup.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.customviews; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | import android.widget.RelativeLayout; 8 | import android.widget.RemoteViews; 9 | import android.widget.TextView; 10 | 11 | import com.dreamfactory.sampleapp.R; 12 | import com.dreamfactory.sampleapp.models.ContactInfoRecord; 13 | 14 | @RemoteViews.RemoteView 15 | public class InfoViewGroup extends RelativeLayout { 16 | 17 | public InfoViewGroup(Context context, ContactInfoRecord record) { 18 | super(context); 19 | LayoutInflater inflater = LayoutInflater.from(context); 20 | 21 | inflater.inflate(R.layout.contact_info_layout, this, true); 22 | 23 | TextView type = (TextView) findViewById(R.id.type_label); 24 | type.setText(record.getInfoType()); 25 | 26 | if (record.getPhone().isEmpty()) { 27 | RelativeLayout layout = (RelativeLayout) findViewById(R.id.info_phone_layout); 28 | layout.setVisibility(GONE); 29 | } else { 30 | TextView phoneLabel = (TextView) findViewById(R.id.phone_label); 31 | phoneLabel.setText(record.getPhone()); 32 | } 33 | 34 | if (record.getEmail().isEmpty()) { 35 | RelativeLayout layout = (RelativeLayout) findViewById(R.id.info_email_layout); 36 | layout.setVisibility(GONE); 37 | } else { 38 | TextView emailLabel = (TextView) findViewById(R.id.email_label); 39 | emailLabel.setText(record.getEmail()); 40 | } 41 | 42 | if (record.getAddress().isEmpty() || record.getCity().isEmpty()) { 43 | RelativeLayout layout = (RelativeLayout) findViewById(R.id.info_address_layout); 44 | layout.setVisibility(GONE); 45 | } else { 46 | TextView addressLabel = (TextView) findViewById(R.id.address_label); 47 | addressLabel.setText(record.getAddress() + " " + record.getCity()); 48 | } 49 | } 50 | 51 | public void removeFromParent (){ 52 | ((ViewGroup) this.getParent()).removeView(this); 53 | } 54 | public InfoViewGroup(Context context, AttributeSet attrs) { 55 | this(context, attrs, 0); 56 | } 57 | 58 | public InfoViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { 59 | super(context, attrs, defStyleAttr); 60 | } 61 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/BaseRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import java.io.Serializable; 4 | 5 | public class BaseRecord implements Serializable { 6 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/ContactInfoRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import android.os.Parcel; 4 | 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | public class ContactInfoRecord extends BaseRecord { 8 | 9 | protected Long id; 10 | 11 | protected Long ordinal = 0L; 12 | 13 | @JsonProperty("contact_id") 14 | protected Long contactId; 15 | 16 | @JsonProperty("info_type") 17 | protected String infoType = ""; 18 | 19 | protected String phone = ""; 20 | 21 | protected String email = ""; 22 | 23 | protected String address = ""; 24 | 25 | protected String city = ""; 26 | 27 | protected String state = ""; 28 | 29 | protected String zip = ""; 30 | 31 | protected String country = ""; 32 | 33 | public ContactInfoRecord(){ 34 | } 35 | 36 | public Long getId() { 37 | return id; 38 | } 39 | 40 | public void setId(Long id) { 41 | this.id = id; 42 | } 43 | 44 | public Long getOrdinal() { 45 | return ordinal; 46 | } 47 | 48 | public void setOrdinal(Long ordinal) { 49 | this.ordinal = ordinal; 50 | } 51 | 52 | public Long getContactId() { 53 | return contactId; 54 | } 55 | 56 | public void setContactId(Long contactId) { 57 | this.contactId = contactId; 58 | } 59 | 60 | public String getInfoType() { 61 | return infoType; 62 | } 63 | 64 | public void setInfoType(String infoType) { 65 | this.infoType = infoType; 66 | } 67 | 68 | public String getPhone() { 69 | return phone; 70 | } 71 | 72 | public void setPhone(String phone) { 73 | this.phone = phone; 74 | } 75 | 76 | public String getEmail() { 77 | return email; 78 | } 79 | 80 | public void setEmail(String email) { 81 | this.email = email; 82 | } 83 | 84 | public String getAddress() { 85 | return address; 86 | } 87 | 88 | public void setAddress(String address) { 89 | this.address = address; 90 | } 91 | 92 | public String getCity() { 93 | return city; 94 | } 95 | 96 | public void setCity(String city) { 97 | this.city = city; 98 | } 99 | 100 | public String getState() { 101 | return state; 102 | } 103 | 104 | public void setState(String state) { 105 | this.state = state; 106 | } 107 | 108 | public String getZip() { 109 | return zip; 110 | } 111 | 112 | public void setZip(String zip) { 113 | this.zip = zip; 114 | } 115 | 116 | public String getCountry() { 117 | return country; 118 | } 119 | 120 | public void setCountry(String country) { 121 | this.country = country; 122 | } 123 | 124 | public static class Parcelable extends ContactInfoRecord implements android.os.Parcelable { 125 | 126 | public Parcelable() { 127 | } 128 | 129 | public Parcelable(ContactInfoRecord record) { 130 | this.id = record.id; 131 | this.ordinal = record.ordinal; 132 | this.contactId = record.contactId; 133 | this.infoType = record.infoType; 134 | this.phone = record.phone; 135 | this.email = record.email; 136 | this.address = record.address; 137 | this.city = record.city; 138 | this.state = record.state; 139 | this.zip = record.zip; 140 | this.country = record.country; 141 | } 142 | 143 | @Override 144 | public int describeContents() { 145 | return 0; 146 | } 147 | 148 | @Override 149 | public void writeToParcel(Parcel dest, int flags) { 150 | dest.writeValue(id); 151 | dest.writeValue(ordinal); 152 | dest.writeValue(contactId); 153 | dest.writeString(infoType); 154 | dest.writeString(phone); 155 | dest.writeString(email); 156 | dest.writeString(address); 157 | dest.writeString(city); 158 | dest.writeString(state); 159 | dest.writeString(zip); 160 | dest.writeString(country); 161 | } 162 | 163 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 164 | public Parcelable createFromParcel(Parcel in) { 165 | return new Parcelable(in); 166 | } 167 | 168 | @Override 169 | public Parcelable[] newArray(int size) { 170 | return new Parcelable[size]; 171 | } 172 | }; 173 | 174 | private Parcelable(Parcel in) { 175 | id = (Long) in.readValue(Long.class.getClassLoader()); 176 | ordinal = (Long) in.readValue(Long.class.getClassLoader()); 177 | contactId = (Long) in.readValue(Long.class.getClassLoader()); 178 | infoType = in.readString(); 179 | phone = in.readString(); 180 | email = in.readString(); 181 | address = in.readString(); 182 | city = in.readString(); 183 | state = in.readString(); 184 | zip = in.readString(); 185 | country = in.readString(); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/ContactRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import android.os.Parcel; 4 | 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | public class ContactRecord extends BaseRecord { 8 | 9 | protected Long id; 10 | 11 | @JsonProperty("first_name") 12 | protected String firstName; 13 | 14 | @JsonProperty("last_name") 15 | protected String lastName; 16 | 17 | @JsonProperty("image_url") 18 | protected String imageUrl; 19 | 20 | protected String twitter; 21 | 22 | protected String skype; 23 | 24 | protected String notes; 25 | 26 | public ContactRecord() { 27 | } 28 | 29 | public Long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Long id) { 34 | this.id = id; 35 | } 36 | 37 | public String getFirstName() { 38 | return firstName != null ? firstName : ""; 39 | } 40 | 41 | public void setFirstName(String firstName) { 42 | this.firstName = firstName; 43 | } 44 | 45 | public String getLastName() { 46 | return lastName != null ? lastName : ""; 47 | } 48 | 49 | public void setLastName(String lastName) { 50 | this.lastName = lastName; 51 | } 52 | 53 | public String getImageUrl() { 54 | return imageUrl != null ? imageUrl : ""; 55 | } 56 | 57 | public void setImageUrl(String imageUrl) { 58 | this.imageUrl = imageUrl; 59 | } 60 | 61 | public String getTwitter() { 62 | return twitter != null ? twitter : ""; 63 | } 64 | 65 | public void setTwitter(String twitter) { 66 | this.twitter = twitter; 67 | } 68 | 69 | public String getSkype() { 70 | return skype != null ? skype : ""; 71 | } 72 | 73 | public void setSkype(String skype) { 74 | this.skype = skype; 75 | } 76 | 77 | public String getNotes() { 78 | return notes != null ? notes : ""; 79 | } 80 | 81 | public void setNotes(String notes) { 82 | this.notes = notes; 83 | } 84 | 85 | public String getFullName() { 86 | return firstName + " " + lastName; 87 | } 88 | 89 | public static class Parcelable extends ContactRecord implements android.os.Parcelable { 90 | 91 | public Parcelable() { 92 | } 93 | 94 | public Parcelable(ContactRecord record) { 95 | this.id = record.id; 96 | this.firstName = record.firstName; 97 | this.lastName = record.lastName; 98 | this.imageUrl = record.imageUrl; 99 | this.twitter = record.twitter; 100 | this.skype = record.skype; 101 | this.notes = record.notes; 102 | } 103 | 104 | @Override 105 | public int describeContents() { 106 | return 0; 107 | } 108 | 109 | @Override 110 | public void writeToParcel(Parcel dest, int flags) { 111 | dest.writeLong(id); 112 | dest.writeString(firstName); 113 | dest.writeString(lastName); 114 | dest.writeString(imageUrl); 115 | dest.writeString(twitter); 116 | dest.writeString(skype); 117 | dest.writeString(notes); 118 | } 119 | 120 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 121 | public Parcelable createFromParcel(Parcel in) { 122 | return new Parcelable(in); 123 | } 124 | 125 | @Override 126 | public Parcelable[] newArray(int size) { 127 | return new Parcelable[size]; 128 | } 129 | }; 130 | 131 | private Parcelable(Parcel in) { 132 | id = in.readLong(); 133 | firstName = in.readString(); 134 | lastName = in.readString(); 135 | imageUrl = in.readString(); 136 | twitter = in.readString(); 137 | skype = in.readString(); 138 | notes = in.readString(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/ContactsRelationalRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class ContactsRelationalRecord extends BaseRecord { 6 | 7 | @JsonProperty("contact_by_contact_id") 8 | private ContactRecord contact; 9 | 10 | @JsonProperty("contact_id") 11 | private Long contactId; 12 | 13 | @JsonProperty("contact_group_id") 14 | private Long contactGroupId; 15 | 16 | public ContactRecord getContact() { 17 | return contact; 18 | } 19 | 20 | public void setContact(ContactRecord contact) { 21 | this.contact = contact; 22 | } 23 | 24 | public Long getContactId() { 25 | return contactId; 26 | } 27 | 28 | public void setContactId(Long contactId) { 29 | this.contactId = contactId; 30 | } 31 | 32 | public Long getContactGroupId() { 33 | return contactGroupId; 34 | } 35 | 36 | public void setContactGroupId(Long contactGroupId) { 37 | this.contactGroupId = contactGroupId; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by Murtic on 03/06/16. 7 | */ 8 | public class ErrorMessage implements Serializable { 9 | 10 | private Error error = new Error(); 11 | 12 | public ErrorMessage() {} 13 | 14 | public ErrorMessage(String message) { 15 | this.error = new Error(message); 16 | } 17 | 18 | public Error getError() { 19 | return error; 20 | } 21 | 22 | public void setError(Error error) { 23 | this.error = error; 24 | } 25 | 26 | public ErrorException toException() { 27 | return new ErrorException(this); 28 | } 29 | 30 | public static class Error implements Serializable { 31 | 32 | private String message; 33 | 34 | private Long code = 0L; 35 | 36 | private String[] trace; 37 | 38 | private Context context; 39 | 40 | public Error() {} 41 | 42 | public Error(String message) { 43 | this.message = message; 44 | } 45 | 46 | public String getMessage() { 47 | return message; 48 | } 49 | 50 | public void setMessage(String message) { 51 | this.message = message; 52 | } 53 | 54 | public Long getCode() { 55 | return code; 56 | } 57 | 58 | public void setCode(Long code) { 59 | this.code = code; 60 | } 61 | 62 | public String[] getTrace() { 63 | return trace; 64 | } 65 | 66 | public void setTrace(String[] trace) { 67 | this.trace = trace; 68 | } 69 | 70 | public Context getContext() { 71 | return context; 72 | } 73 | 74 | public void setContext(Context context) { 75 | this.context = context; 76 | } 77 | 78 | public static class Context implements Serializable { 79 | 80 | private String[] email = new String[0]; 81 | 82 | private String[] password = new String[0]; 83 | 84 | public Context() { } 85 | 86 | public String[] getEmail() { 87 | return email; 88 | } 89 | 90 | public void setEmail(String[] email) { 91 | this.email = email; 92 | } 93 | 94 | public String[] getPassword() { 95 | return password; 96 | } 97 | 98 | public void setPassword(String[] password) { 99 | this.password = password; 100 | } 101 | } 102 | } 103 | 104 | public static class ErrorException extends Exception { 105 | private ErrorMessage errorMessage; 106 | 107 | public ErrorException() { 108 | } 109 | 110 | public ErrorException(ErrorMessage errorMessage) { 111 | super(errorMessage.getError().getMessage()); 112 | 113 | this.errorMessage = errorMessage != null ? errorMessage : new ErrorMessage("Unexpected error"); 114 | } 115 | 116 | public ErrorMessage getErrorMessage() { 117 | return errorMessage; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/FileRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | /** 6 | * Created by Nirmel on 6/7/2016. 7 | */ 8 | public class FileRecord extends BaseRecord { 9 | 10 | private String path; 11 | 12 | @JsonProperty("last_modified") 13 | private String lastModified; 14 | 15 | private String name; 16 | 17 | private String type; 18 | 19 | @JsonProperty("content_type") 20 | private String contentType; 21 | 22 | @JsonProperty("content_length") 23 | private String contentLength; 24 | 25 | private String content; 26 | 27 | public FileRecord() { 28 | } 29 | 30 | public String getPath() { 31 | return path; 32 | } 33 | 34 | public void setPath(String path) { 35 | this.path = path; 36 | } 37 | 38 | public String getType() { 39 | return type; 40 | } 41 | 42 | public void setType(String type) { 43 | this.type = type; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | 54 | public String getLastModified() { 55 | return lastModified; 56 | } 57 | 58 | public void setLastModified(String lastModified) { 59 | this.lastModified = lastModified; 60 | } 61 | 62 | public String getContentType() { 63 | return contentType; 64 | } 65 | 66 | public void setContentType(String contentType) { 67 | this.contentType = contentType; 68 | } 69 | 70 | public String getContentLength() { 71 | return contentLength; 72 | } 73 | 74 | public void setContentLength(String contentLength) { 75 | this.contentLength = contentLength; 76 | } 77 | 78 | public String getContent() { 79 | return content; 80 | } 81 | 82 | public void setContent(String content) { 83 | this.content = content; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/FileRequest.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import java.util.*; 5 | 6 | /** 7 | * Used for sending a file request to the server 8 | */ 9 | public class FileRequest { 10 | /* Identifier/Name for the file, localized to requested resource. */ 11 | @JsonProperty("name") 12 | private String name = null; 13 | /* Full path of the file, from the service including container. */ 14 | @JsonProperty("path") 15 | private String path = null; 16 | /* The media type of the content of the file. */ 17 | @JsonProperty("contentType") 18 | private String contentType = null; 19 | /* An array of name-value pairs. */ 20 | @JsonProperty("metadata") 21 | 22 | private List metadata = new ArrayList<>(); 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | 31 | public String getPath() { 32 | return path; 33 | } 34 | public void setPath(String path) { 35 | this.path = path; 36 | } 37 | 38 | public String getContentType() { 39 | return contentType; 40 | } 41 | public void setContentType(String contentType) { 42 | this.contentType = contentType; 43 | } 44 | 45 | public List getMetadata() { 46 | return metadata; 47 | } 48 | public void setMetadata(List metadata) { 49 | this.metadata = metadata; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "class FileRequest {\n" + 55 | " name: " + name + "\n" + 56 | " path: " + path + "\n" + 57 | " contentType: " + contentType + "\n" + 58 | " metadata: " + metadata + "\n" + 59 | "}\n"; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/GroupRecord.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | public class GroupRecord extends BaseRecord { 4 | 5 | private Long id; 6 | 7 | private String name = ""; 8 | 9 | public Long getId() { 10 | return id; 11 | } 12 | 13 | public void setId(Long id) { 14 | this.id = id; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/RegisterResponse.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Created by Nirmel on 6/3/2016. 9 | */ 10 | public class RegisterResponse implements Serializable { 11 | 12 | private Boolean success; 13 | 14 | @JsonProperty("session_token") 15 | private String sessionToken; 16 | 17 | public Boolean getSuccess() { 18 | return success; 19 | } 20 | 21 | public void setSuccess(Boolean success) { 22 | this.success = success; 23 | } 24 | 25 | public String getSessionToken() { 26 | return sessionToken; 27 | } 28 | 29 | public void setSessionToken(String sessionToken) { 30 | this.sessionToken = sessionToken; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/Resource.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import android.os.Parcel; 4 | 5 | import java.io.Serializable; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Nirmel on 6/3/2016. 11 | */ 12 | public class Resource implements Serializable { 13 | 14 | protected List resource = new ArrayList<>(); 15 | 16 | public List getResource() { 17 | return resource; 18 | } 19 | 20 | public void setResource(List resource) { 21 | this.resource = resource; 22 | } 23 | 24 | public void addResource(T value) { 25 | resource.add(value); 26 | } 27 | 28 | public Resource() { 29 | } 30 | 31 | public static class Parcelable extends Resource implements android.os.Parcelable { 32 | 33 | public Parcelable() { 34 | } 35 | 36 | @Override 37 | public int describeContents() { 38 | return 0; 39 | } 40 | 41 | @Override 42 | public void writeToParcel(Parcel dest, int flags) { 43 | dest.writeInt(resource.size()); 44 | 45 | if(resource.size() > 0) { 46 | dest.writeValue(resource.get(0).getClass()); 47 | } 48 | 49 | for(T record : resource){ 50 | dest.writeParcelable((android.os.Parcelable) record, flags); 51 | } 52 | } 53 | 54 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 55 | public Parcelable createFromParcel(Parcel in) { 56 | return new Parcelable(in); 57 | } 58 | 59 | @Override 60 | public Parcelable[] newArray(int size) { 61 | return new Parcelable[size]; 62 | } 63 | }; 64 | 65 | private Parcelable(Parcel in) { 66 | int size = in.readInt(); 67 | 68 | resource = new ArrayList<>(); 69 | 70 | if(size > 0) { 71 | Class c = (Class) in.readValue(null); 72 | 73 | for(int i = 0; i < size; i++){ 74 | T value = in.readParcelable(c.getClassLoader()); 75 | 76 | resource.add(value); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/User.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models; 2 | 3 | import com.dreamfactory.sampleapp.utils.CustomJsonDateDeserializer; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | 7 | import java.util.Date; 8 | 9 | /** 10 | * Created by Nirmel on 6/3/2016. 11 | */ 12 | public class User extends BaseRecord { 13 | 14 | @JsonProperty("session_token") 15 | private String sessionToken; 16 | 17 | @JsonProperty("session_id") 18 | private String sessionId; 19 | 20 | private Long id; 21 | 22 | private String name; 23 | 24 | @JsonProperty("first_name") 25 | private String firstName; 26 | 27 | @JsonProperty("last_name") 28 | private String lastName; 29 | 30 | private String email; 31 | 32 | @JsonProperty("is_sys_admin") 33 | private Boolean isSysAdmin; 34 | 35 | @JsonProperty("last_login_date") 36 | @JsonDeserialize(using = CustomJsonDateDeserializer.class) 37 | private Date lastLoginData; 38 | 39 | private String host; 40 | 41 | private String role; 42 | 43 | @JsonProperty("role_id") 44 | private Long roleId; 45 | 46 | public String getSessionToken() { 47 | return sessionToken; 48 | } 49 | 50 | public void setSessionToken(String sessionToken) { 51 | this.sessionToken = sessionToken; 52 | } 53 | 54 | public String getSessionId() { 55 | return sessionId; 56 | } 57 | 58 | public void setSessionId(String sessionId) { 59 | this.sessionId = sessionId; 60 | } 61 | 62 | public Long getId() { 63 | return id; 64 | } 65 | 66 | public void setId(Long id) { 67 | this.id = id; 68 | } 69 | 70 | public String getName() { 71 | return name; 72 | } 73 | 74 | public void setName(String name) { 75 | this.name = name; 76 | } 77 | 78 | public String getFirstName() { 79 | return firstName; 80 | } 81 | 82 | public void setFirstName(String firstName) { 83 | this.firstName = firstName; 84 | } 85 | 86 | public String getLastName() { 87 | return lastName; 88 | } 89 | 90 | public void setLastName(String lastName) { 91 | this.lastName = lastName; 92 | } 93 | 94 | public String getEmail() { 95 | return email; 96 | } 97 | 98 | public void setEmail(String email) { 99 | this.email = email; 100 | } 101 | 102 | public Boolean getSysAdmin() { 103 | return isSysAdmin; 104 | } 105 | 106 | public void setSysAdmin(Boolean sysAdmin) { 107 | isSysAdmin = sysAdmin; 108 | } 109 | 110 | public Date getLastLoginData() { 111 | return lastLoginData; 112 | } 113 | 114 | public void setLastLoginData(Date lastLoginData) { 115 | this.lastLoginData = lastLoginData; 116 | } 117 | 118 | public String getHost() { 119 | return host; 120 | } 121 | 122 | public void setHost(String host) { 123 | this.host = host; 124 | } 125 | 126 | public String getRole() { 127 | return role; 128 | } 129 | 130 | public void setRole(String role) { 131 | this.role = role; 132 | } 133 | 134 | public Long getRoleId() { 135 | return roleId; 136 | } 137 | 138 | public void setRoleId(Long roleId) { 139 | this.roleId = roleId; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/requests/LoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models.requests; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by Nirmel on 6/3/2016. 7 | */ 8 | public class LoginRequest implements Serializable { 9 | 10 | private String email; 11 | 12 | private String password; 13 | 14 | public String getEmail() { 15 | return email; 16 | } 17 | 18 | public void setEmail(String email) { 19 | this.email = email; 20 | } 21 | 22 | public String getPassword() { 23 | return password; 24 | } 25 | 26 | public void setPassword(String password) { 27 | this.password = password; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/models/requests/RegisterRequest.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.models.requests; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Created by Nirmel on 6/3/2016. 9 | */ 10 | public class RegisterRequest implements Serializable { 11 | 12 | private String email; 13 | 14 | private String password; 15 | 16 | @JsonProperty("first_name") 17 | private String firstName; 18 | 19 | @JsonProperty("last_name") 20 | private String lastName; 21 | 22 | private String name; 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public void setName(String name) { 29 | this.name = name; 30 | } 31 | 32 | public String getEmail() { 33 | return email; 34 | } 35 | 36 | public void setEmail(String email) { 37 | this.email = email; 38 | } 39 | 40 | public String getPassword() { 41 | return password; 42 | } 43 | 44 | public void setPassword(String password) { 45 | this.password = password; 46 | } 47 | 48 | public String getFirstName() { 49 | return firstName; 50 | } 51 | 52 | public void setFirstName(String firstName) { 53 | this.firstName = firstName; 54 | } 55 | 56 | public String getLastName() { 57 | return lastName; 58 | } 59 | 60 | public void setLastName(String lastName) { 61 | this.lastName = lastName; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/utils/CustomJsonDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.utils; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | /** 13 | * Created by Nirmel on 6/3/2016. 14 | */ 15 | public class CustomJsonDateDeserializer extends JsonDeserializer 16 | { 17 | private static String PATTERN = "yyyy-MM-dd HH:mm:ss"; 18 | 19 | @Override 20 | public Date deserialize(JsonParser jsonparser, 21 | DeserializationContext deserializationcontext) throws IOException { 22 | 23 | SimpleDateFormat format = new SimpleDateFormat(PATTERN); 24 | String date = jsonparser.getText(); 25 | try { 26 | return format.parse(date); 27 | } catch (ParseException e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/utils/ImageUtil.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.utils; 2 | 3 | import android.content.ContentResolver; 4 | import android.database.Cursor; 5 | import android.net.Uri; 6 | import android.os.Build; 7 | import android.provider.DocumentsContract; 8 | import android.provider.MediaStore; 9 | import android.util.Log; 10 | 11 | import com.dreamfactory.sampleapp.activities.CreateContactActivity; 12 | 13 | /** 14 | * Created by Murtic on 25/06/16. 15 | */ 16 | public class ImageUtil { 17 | 18 | public static String getImagePath(Uri uri, ContentResolver contentResolver) { 19 | String profileImagePath = null; 20 | 21 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 22 | try { 23 | String wholeID = DocumentsContract.getDocumentId(uri); 24 | String[] column = {MediaStore.Images.Media.DATA}; 25 | 26 | // Split at colon, use second item in the array 27 | String id = wholeID.split(":")[1]; 28 | 29 | // where id is equal to 30 | String sel = MediaStore.Images.Media._ID + "=?"; 31 | 32 | Cursor cursor = contentResolver.query( 33 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 34 | column, sel, new String[]{id}, null); 35 | 36 | int columnIndex = cursor.getColumnIndex(column[0]); 37 | 38 | if (cursor.moveToFirst()) { 39 | profileImagePath = cursor.getString(columnIndex); 40 | } 41 | cursor.close(); 42 | } catch (Exception e) { 43 | Log.e(CreateContactActivity.class.getSimpleName(), "could not decode image: " + e.toString()); 44 | } 45 | } else { 46 | profileImagePath = uri.getPath(); 47 | } 48 | 49 | return profileImagePath; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/java/com/dreamfactory/sampleapp/utils/PrefUtil.java: -------------------------------------------------------------------------------- 1 | package com.dreamfactory.sampleapp.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | public class PrefUtil { 7 | static public final class Prefs { 8 | public static SharedPreferences get(Context context) { 9 | return context.getSharedPreferences("_dreamf_pref", 0); 10 | } 11 | } 12 | 13 | static public String getString(Context context, String key) { 14 | SharedPreferences settings = Prefs.get(context); 15 | return settings.getString(key, ""); 16 | } 17 | 18 | static public String getString(Context context, String key, String defaultString) { 19 | SharedPreferences settings = Prefs.get(context); 20 | return settings.getString(key, defaultString); 21 | } 22 | 23 | static public synchronized void putString(Context context, String key, String value) { 24 | SharedPreferences settings = Prefs.get(context); 25 | SharedPreferences.Editor editor = settings.edit(); 26 | editor.putString(key, value); 27 | editor.apply(); 28 | } 29 | } -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/add_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/back_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 12 | 13 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/default_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/default_portrait.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/df_logo_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/df_logo_filled.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/done_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/edit_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/home.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/ic_add_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/ic_add_black_24dp.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/ic_arrow_back_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/ic_arrow_back_black_24dp.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/ic_check_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/ic_check_black_24dp.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/ic_delete_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/ic_delete_black_24dp.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/ic_mode_edit_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/ic_mode_edit_black_24dp.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/mail.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/drawable/phone1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/drawable/phone1.png -------------------------------------------------------------------------------- /android-sdk/app/src/main/res/layout/activity_choose_image.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 17 |