├── .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 |
24 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_contact_list.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_contact_view.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
21 |
27 |
36 |
37 |
45 |
46 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_edit_contact.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
26 |
27 |
28 |
31 |
38 |
39 |
40 |
50 |
60 |
70 |
80 |
90 |
96 |
97 |
98 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_group.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
19 |
20 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_group_list.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
22 |
23 |
28 |
29 |
31 |
32 |
36 |
37 |
43 |
44 |
48 |
49 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/contact_info_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
29 |
30 |
31 |
36 |
37 |
46 |
60 |
61 |
62 |
63 |
64 |
69 |
79 |
93 |
94 |
95 |
96 |
97 |
102 |
113 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/edit_contact_info_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
27 |
35 |
43 |
51 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/persistent_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
30 |
31 |
52 |
53 |
76 |
77 |
78 |
101 |
102 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/rowlayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/layout/selcetable_row_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
27 |
28 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_choose_image.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_contact_list.xml:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_contact_view.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_create_group.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_edit_contact.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_group_list.xml:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #7093B5
4 | #FDFDFD
5 |
6 | #F0F0F0
7 | #F9982B
8 | #D87A27
9 | #F9982B
10 | #D87A27
11 | #6DA1B3
12 | #2B687C
13 |
14 | #FDFDFD
15 | #D2E1EF
16 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | android-sdk
3 | GroupListActivity
4 |
5 | Hello world!
6 | Settings
7 | MainActivity
8 | ContactListActivity
9 | ContactViewActivity
10 | EditContactActivity
11 | CreateContactActivity
12 | GroupActivity
13 | ChooseImageActivity
14 | RegisterActivity
15 |
16 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/strings_activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Email
5 | Password (optional)
6 | Sign in
7 | Sign in
8 |
9 | This email address is invalid
10 | This password is too short
11 | This password is incorrect
12 | This field is required
13 |
14 |
--------------------------------------------------------------------------------
/android-sdk/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android-sdk/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android-sdk/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 |
--------------------------------------------------------------------------------
/android-sdk/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/android-sdk/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android-sdk/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 03 00:33:01 CEST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/android-sdk/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/android-sdk/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android-sdk/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/app-debug.apk
--------------------------------------------------------------------------------
/package/add_android.dfpkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/android-sdk/e946aadc8a49cd508b73c633c677cb947bec1aa5/package/add_android.dfpkg
--------------------------------------------------------------------------------
/package/description.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Address Book for Android",
3 | "description": "An address book app for Android showing user registration, user login, and CRUD.",
4 | "type": 0,
5 | "is_active": true
6 | }
--------------------------------------------------------------------------------
/package/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "service": [
3 | {
4 | "name": "db",
5 | "table": [
6 | {
7 | "description": "The main table for tracking contacts.",
8 | "name": "contact",
9 | "field": [
10 | {
11 | "name": "id",
12 | "label": "Contact Id",
13 | "type": "id"
14 | },
15 | {
16 | "name": "first_name",
17 | "type": "string",
18 | "size": 40,
19 | "allow_null": false,
20 | "is_index": true,
21 | "validation": {
22 | "not_empty": {
23 | "on_fail": "First name value must not be empty."
24 | }
25 | }
26 | },
27 | {
28 | "name": "last_name",
29 | "type": "string",
30 | "size": 40,
31 | "allow_null": false,
32 | "is_index": true,
33 | "validation": {
34 | "not_empty": {
35 | "on_fail": "Last name value must not be empty."
36 | }
37 | }
38 | },
39 | {
40 | "name": "image_url",
41 | "label": "image_url",
42 | "type": "text",
43 | "validation": {
44 | "url": {
45 | "on_fail": "Not a valid URL value."
46 | }
47 | },
48 | "allow_null": true
49 | },
50 | {
51 | "name": "twitter",
52 | "label": "Twitter Handle",
53 | "type": "string",
54 | "size": 18,
55 | "allow_null": true
56 | },
57 | {
58 | "name": "skype",
59 | "label": "Skype Account",
60 | "type": "string",
61 | "size": 255,
62 | "allow_null": true
63 | },
64 | {
65 | "name": "notes",
66 | "label": "notes",
67 | "type": "text",
68 | "allow_null": true
69 | }
70 | ]
71 | },
72 | {
73 | "description": "The contact details sub-table, owned by contact table row.",
74 | "name": "contact_info",
75 | "field": [
76 | {
77 | "name": "id",
78 | "label": "Info Id",
79 | "type": "id"
80 | },
81 | {
82 | "name": "ordinal",
83 | "type": "integer",
84 | "allow_null": true
85 | },
86 | {
87 | "name": "contact_id",
88 | "type": "reference",
89 | "allow_null": false,
90 | "ref_table": "contact",
91 | "ref_fields": "id",
92 | "ref_on_delete": "CASCADE"
93 | },
94 | {
95 | "name": "info_type",
96 | "type": "string",
97 | "size": 32,
98 | "allow_null": false,
99 | "validation": {
100 | "not_empty": {
101 | "on_fail": "Information type can not be empty."
102 | },
103 | "picklist": {
104 | "on_fail": "Not a valid information type."
105 | }
106 | },
107 | "picklist": [
108 | "work",
109 | "home",
110 | "mobile",
111 | "other"
112 | ]
113 | },
114 | {
115 | "name": "phone",
116 | "label": "Phone Number",
117 | "type": "string",
118 | "size": 32
119 | },
120 | {
121 | "name": "email",
122 | "label": "Email Address",
123 | "type": "string",
124 | "size": 255,
125 | "validation": {
126 | "email": {
127 | "on_fail": "Not a valid email address."
128 | }
129 | }
130 | },
131 | {
132 | "name": "address",
133 | "label": "Street Address",
134 | "type": "string"
135 | },
136 | {
137 | "name": "city",
138 | "label": "city",
139 | "type": "string",
140 | "size": 64
141 | },
142 | {
143 | "name": "state",
144 | "label": "state",
145 | "type": "string",
146 | "size": 64
147 | },
148 | {
149 | "name": "zip",
150 | "label": "zip",
151 | "type": "string",
152 | "size": 16
153 | },
154 | {
155 | "name": "country",
156 | "label": "country",
157 | "type": "string",
158 | "size": 64
159 | }
160 | ]
161 | },
162 | {
163 | "description": "The main table for tracking groups of contact.",
164 | "name": "contact_group",
165 | "field": [
166 | {
167 | "name": "id",
168 | "type": "id"
169 | },
170 | {
171 | "name": "name",
172 | "type": "string",
173 | "size": 128,
174 | "allow_null": false,
175 | "validation": {
176 | "not_empty": {
177 | "on_fail": "Group name value must not be empty."
178 | }
179 | }
180 | }
181 | ]
182 | },
183 | {
184 | "description": "The join table for tracking contacts in groups.",
185 | "name": "contact_group_relationship",
186 | "field": [
187 | {
188 | "name": "id",
189 | "type": "id"
190 | },
191 | {
192 | "name": "contact_id",
193 | "type": "reference",
194 | "allow_null": false,
195 | "ref_table": "contact",
196 | "ref_fields": "id",
197 | "ref_on_delete": "CASCADE"
198 | },
199 | {
200 | "name": "contact_group_id",
201 | "type": "reference",
202 | "allow_null": false,
203 | "ref_table": "contact_group",
204 | "ref_fields": "id",
205 | "ref_on_delete": "CASCADE"
206 | }
207 | ]
208 | }
209 | ]
210 | }
211 | ]
212 | }
213 |
--------------------------------------------------------------------------------