4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | PhotoAlbum
4 |
5 |
6 | CloudinaryAndroid
7 |
8 |
9 |
10 | com.android.ide.eclipse.adt.ResourceManagerBuilder
11 |
12 |
13 |
14 |
15 | com.android.ide.eclipse.adt.PreCompilerBuilder
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javabuilder
21 |
22 |
23 |
24 |
25 | com.android.ide.eclipse.adt.ApkBuilder
26 |
27 |
28 |
29 |
30 |
31 | com.android.ide.eclipse.adt.AndroidNature
32 | org.eclipse.jdt.core.javanature
33 |
34 |
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :warning: Following [Parse's end of service notification](http://blog.parse.com/announcements/moving-on/), this repo is deprecated and is not supported. The issues section won't be available.
2 |
3 |
4 | Cloudinary, Android and Parse - Photo Album sample
5 | ==================================================
6 |
7 | Demo for writing an Android application using Parse as a backend and Cloudinary as image backend for storing,
8 | applying transformations and serving of images.
9 |
10 | For more details about **Cloudinary**: http://cloudinary.com/
11 |
12 | For more details about **Android development**: http://developer.android.com/develop/index.html
13 |
14 | For more details about **Parse**: https://www.parse.com/
15 |
16 |
17 | ## Setup and run the sample project
18 |
19 | * [Setup the Parse backend in 7 simple steps](https://github.com/cloudinary/cloudinary_parse#setup-the-sample-project)
20 | * Setup a working [Android Studio](http://developer.android.com/sdk/index.html).
21 | * Clone or [download](https://github.com/cloudinary/cloudinary-android-parse-sample/archive/master.zip) this repository
22 | * Copy the `AndroidManifest.xml.sample` file into `AndroidManifest.xml` and modify the meta-data fields within it to reflect your Cloudinary cloud name and your Parse Application ID and Client Key.
23 | * Import your project (from Android Studio welcome screen > "Open an existing Android Studio project" -> [Choose the cloudinary-android-parse-sample path])
24 | * Run the application on an Android device or a simulator (Run -> Run 'App' -> [Choose device to run on])
25 |
26 | ## How does it work
27 | The application is composed of 5 android activities (`SplashScreenActivity`, `LoginActivity`, `ListPhotoActivity`, `ShowPhotoActivity` and `UploadPhotoActivity`), the application singleton (`PhotoAlbumApplication`), a few helper classes and a few external libraries.
28 |
29 | ### Upload process
30 | The Cloudinary API key and API secret are protected in Parse cloud code. They are not bundled in the application and are not accessible by any user - making the design more secure and protecting yours and your users' privacy and data integrity.
31 |
32 | In order to upload in image from the application:
33 |
34 | * A request to upload is sent by the application to Parse backend using a cloud function.
35 | * The cloud function (by default, `sign_upload_request`) creates a signed request (using Cloudinary API parameters) and returns it to the application.
36 | * The application uploads the image to Cloudinary using the retrieved signed request. When the upload is complete, Cloudinary returns a signed result of the operation containing the image public\_id, other identifiers required to access the image and metadata about the image (disk size, dimensions, etc...)
37 | * The application saves the signed reference of the image to Parse backend.
38 | * The Parse beforeSave filter verifies the authenticity of the saved reference and aborts the process if it's invalid.
39 |
40 | ### PhotoAlbumApplication.java
41 | Contains initializations of Universal Image Loader (UIL), Parse and Cloudinary. Cloudinary is initialized using the `CLOUDINARY_URL` meta-data entry in AndroidManifest.xml by passing the `Context` to the Cloudinary constructor
42 |
43 | ### Activities
44 | #### SplashScreenActivity.java
45 | Displays a splash-screen and then launches the LoginActivity
46 |
47 | #### LoginActivity.java
48 | Displays a login screen. The Chuck Norris image demonstrates the usage of Cloudinary Facebook profile image retrieval (See [DownloadImageTask.java](#downloadimagetaskjava) for more information about the image download code)
49 | After the user has provided a username and a password, invokes a task which tries to register (if username doesn't exist yet) or login on behalf of the user using Parse.
50 |
51 | #### ListPhotoActivity.java
52 | Displays an endless list of uploaded images. The list is fetched from Parse by querying the `PARSE_MODEL`. The images themselves are retrieved from Cloudinary.
53 | When an image is clicked, the `ShowPhotoActivity` is launched passing it the image identifier in the intent extra parameters.
54 | This activity also has a menu with upload, refresh and logout actions.
55 |
56 | Based partially on [ImageGridActivity.java](https://github.com/nostra13/Android-Universal-Image-Loader/blob/v1.9.2/sample/src/com/nostra13/example/universalimageloader/ImageGridActivity.java) from Universal Image Loader
57 |
58 | #### ShowPhotoActivity.java
59 | Displays a slidable pager with different transformations of the selected photo.
60 |
61 | Based on [Android Blank Activity with Scrollable Tabs + Swipe template](http://developer.android.com/tools/projects/templates.html#blank-activity)
62 |
63 | #### UploadPhotoActivity.java
64 | Sends an image selection intent to allow the user to select an image to upload.
65 | Once the user selects an image, the [upload process](#uploadprocess) begins.
66 |
67 | ### Helper classes
68 | #### Constants.java
69 | Contains several configuration parameters.
70 |
71 | #### DownloadImageTask.java
72 | A simple but useful AsyncTask which fetches an image by given URL into a given ImageView in the background
73 |
74 | #### L.java
75 | Logging helper class
76 |
77 | ### Libraries
78 | #### cloudinary-android:1.1.2 (*gradle)
79 | The Cloudinary library used to upload and retrieve Cloudinary images
80 |
81 | #### Parse-1.8.1.jar
82 | The Parse library used for user management (sign-up, login), and image objects management (add, list, fetch)
83 |
84 | #### universalimageloader:universal-image-loader:1.9.3 (*gradle)
85 | A very comprehensive and flexible library for ["asynchronous image loading, caching and displaying"](https://github.com/nostra13/Android-Universal-Image-Loader#-universal-image-loader-for-android)
86 |
87 | ## Read more
88 |
89 | [Cloudinary documentation](http://cloudinary.com/documentation)
90 | [Cloudinary for Android](https://github.com/cloudinary/cloudinary_android)
91 | [Cloudinary for Parse](https://github.com/cloudinary/cloudinary_parse)
92 |
93 | # Support
94 |
95 | You can [open an issue through GitHub](https://github.com/cloudinary/cloudinary-android-parse-sample/issues).
96 |
97 | Contact us at [info@cloudinary.com](mailto:info@cloudinary.com)
98 |
99 | Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary)
100 |
101 | ## Licenses
102 |
103 | * This project is released under the MIT license.
104 | * [Parse third-party licenses](https://github.com/cloudinary/cloudinary-android-parse-sample/blob/master/third_party_licenses.txt)
105 | * [Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader) is distributed under [the Apache License Version 2.0](https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/LICENSE)
106 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 21
5 | buildToolsVersion "21.1.2"
6 |
7 | defaultConfig {
8 | applicationId "com.cloudinary.photoalbum.PhotoAlbumApplication"
9 | minSdkVersion 17
10 | targetSdkVersion 21
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile 'com.android.support:support-v4:21.0.3'
24 | compile 'com.cloudinary:cloudinary-android:1.1.2'
25 | compile 'com.parse.bolts:bolts-android:1.1.4'
26 | compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
27 | compile files('libs/Parse-1.8.1.jar')
28 | compile fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/libs/Parse-1.8.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary_android_parse_sample/8f0e62bf92186e3451e62de9f61388e0a36186fb/app/libs/Parse-1.8.1.jar
--------------------------------------------------------------------------------
/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 ${sdk.dir}/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 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml.sample:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
42 |
45 |
46 |
49 |
50 |
54 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/Constants.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | public final class Constants {
4 | private Constants() {
5 | }
6 | public static final String TAG = "CloudinarySample";
7 | public static final String EXTRA_PHOTO = "com.cloudinary.photo";
8 | public static final String PARSE_MODEL = "Photo";
9 | public static final String PARSE_CLOUDINARY_FIELD = "cloudinaryIdentifier";
10 | public static final String PARSE_SIGN_CLOUD_FUNCTION = "sign_cloudinary_upload_request";
11 | public static final int SPLASH_SCREEN_TIMEOUT = 1000;
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/DownloadImageTask.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import java.io.InputStream;
4 |
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.os.AsyncTask;
8 | import android.widget.ImageView;
9 |
10 | /**
11 | * A simple task that download an image from a given url into an Image View
12 | * Based on http://stackoverflow.com/a/9288544/967435
13 | */
14 | public class DownloadImageTask extends AsyncTask {
15 | ImageView bmImage;
16 |
17 | public DownloadImageTask(ImageView bmImage) {
18 | this.bmImage = bmImage;
19 | }
20 |
21 | protected Bitmap doInBackground(String... urls) {
22 | String urldisplay = urls[0];
23 | Bitmap bitmap = null;
24 | try {
25 | InputStream in = new java.net.URL(urldisplay).openStream();
26 | bitmap = BitmapFactory.decodeStream(in);
27 | } catch (Exception e) {
28 | L.e(e, "Error fetching image");
29 | }
30 | return bitmap;
31 | }
32 |
33 | protected void onPostExecute(Bitmap result) {
34 | bmImage.setImageBitmap(result);
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/L.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * "Less-word" analog of Android {@link Log logger}
7 | *
8 | * Based on Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)
9 | */
10 | public final class L {
11 |
12 | private static final String LOG_FORMAT = "%1$s\n%2$s";
13 | private static String logTag = "uninitialized";
14 |
15 | private L() {
16 | }
17 |
18 | public static void setTag(String tag) {
19 | logTag = tag;
20 | }
21 |
22 | public static void d(String message, Object... args) {
23 | log(Log.DEBUG, null, message, args);
24 | }
25 |
26 | public static void i(String message, Object... args) {
27 | log(Log.INFO, null, message, args);
28 | }
29 |
30 | public static void w(String message, Object... args) {
31 | log(Log.WARN, null, message, args);
32 | }
33 |
34 | public static void e(Throwable ex) {
35 | log(Log.ERROR, ex, null);
36 | }
37 |
38 | public static void e(String message, Object... args) {
39 | log(Log.ERROR, null, message, args);
40 | }
41 |
42 | public static void e(Throwable ex, String message, Object... args) {
43 | log(Log.ERROR, ex, message, args);
44 | }
45 |
46 | private static void log(int priority, Throwable ex, String message, Object... args) {
47 | if (args.length > 0) {
48 | message = String.format(message, args);
49 | }
50 |
51 | String log;
52 | if (ex == null) {
53 | log = message;
54 | } else {
55 | String logMessage = message == null ? ex.getMessage() : message;
56 | String logBody = Log.getStackTraceString(ex);
57 | log = String.format(LOG_FORMAT, logMessage, logBody);
58 | }
59 | Log.println(priority, logTag, log);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/ListPhotosActivity.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import java.util.List;
4 |
5 | import android.app.Activity;
6 | import android.app.AlertDialog;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.os.Bundle;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.AdapterView;
15 | import android.widget.AdapterView.OnItemClickListener;
16 | import android.widget.BaseAdapter;
17 | import android.widget.GridView;
18 | import android.widget.ImageView;
19 |
20 | import com.cloudinary.Cloudinary;
21 | import com.cloudinary.Transformation;
22 | import com.nostra13.universalimageloader.core.ImageLoader;
23 | import com.parse.ParseException;
24 | import com.parse.ParseObject;
25 | import com.parse.ParseQuery;
26 | import com.parse.ParseQuery.CachePolicy;
27 |
28 | public class ListPhotosActivity extends Activity {
29 | static final int REQUEST_UPLOAD = 1;
30 |
31 | protected Cloudinary cloudinary;
32 | protected ImageLoader imageLoader = ImageLoader.getInstance();
33 | protected ParseImageAdapter adapter;
34 | protected GridView listView;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 |
40 | // Cloudinary: Retrieve and save initialized Cloudinary instance
41 | cloudinary = PhotoAlbumApplication.getInstance(this).getCloudinary();
42 |
43 | setContentView(R.layout.activity_list_photos);
44 | listView = (GridView) findViewById(R.id.gridView1);
45 | adapter = new ParseImageAdapter();
46 | listView.setAdapter(adapter);
47 | listView.setOnItemClickListener(new OnItemClickListener() {
48 | @Override
49 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
50 | try {
51 | showImage(adapter.getIdentifier(position));
52 | } catch (ParseException e) {
53 | L.e(e, "Error getting identifier");
54 | errorMessage("Error getting identifier for image to show transformations: " + e.toString());
55 | }
56 |
57 | }
58 | });
59 | }
60 |
61 | private void errorMessage(String errorMessage) {
62 | new AlertDialog.Builder(this)
63 | .setTitle("Error")
64 | .setMessage(errorMessage)
65 | .setPositiveButton("OK",new DialogInterface.OnClickListener() {
66 | public void onClick(DialogInterface dialog, int whichButton) {
67 | finish();
68 | }
69 | })
70 | .setCancelable(true)
71 | .create().show();
72 | }
73 |
74 | private void showImage(String identifier) {
75 | Intent intent = new Intent(this, ShowPhotoActivity.class);
76 | intent.putExtra(Constants.EXTRA_PHOTO, identifier);
77 | startActivity(intent);
78 | }
79 |
80 | @Override
81 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
82 | if (requestCode == REQUEST_UPLOAD && resultCode == RESULT_OK) {
83 | adapter.clearCache();
84 | }
85 | }
86 |
87 | @Override
88 | public boolean onCreateOptionsMenu(Menu menu) {
89 | // Inflate the menu; this adds items to the action bar if it is present.
90 | getMenuInflater().inflate(R.menu.list_photos, menu);
91 | return true;
92 | }
93 |
94 | @Override
95 | public boolean onOptionsItemSelected(MenuItem item) {
96 | Intent intent;
97 | switch (item.getItemId()) {
98 | case R.id.action_upload:
99 | intent = new Intent(this, UploadPhotoActivity.class);
100 | startActivityForResult(intent, REQUEST_UPLOAD);
101 | break;
102 | case R.id.action_refresh:
103 | adapter.clearCache();
104 | break;
105 | case R.id.action_logout:
106 | intent = new Intent(this, LoginActivity.class);
107 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
108 | startActivity(intent);
109 | break;
110 | }
111 | return false;
112 | }
113 |
114 | public class ParseImageAdapter extends BaseAdapter {
115 | private static final int ITEM_PER_FETCH = 20;
116 | private ParseQuery query;
117 | private int cachePosition;
118 | private List cache = null;
119 | private Transformation thumbnailTransformation = new Transformation().width(120).height(120).crop("fill");
120 |
121 | public ParseImageAdapter() {
122 | createQuery();
123 | }
124 |
125 | private void createQuery() {
126 | // Parse: Create a query for model Photo and set caching options
127 | query = ParseQuery.getQuery(Constants.PARSE_MODEL);
128 | query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);
129 | }
130 |
131 | private String getIdentifier(int index) throws ParseException {
132 | index = getCount() - 1 - index;
133 | int base = index-(index%ITEM_PER_FETCH);
134 | if (cache == null || cachePosition != base) {
135 | L.d("Fetching %d items since %d", ITEM_PER_FETCH, base);
136 | // Parse: Fetch ITEM_PER_FETCH items since index "base"
137 | query.setSkip(base);
138 | query.setLimit(ITEM_PER_FETCH);
139 | cache = query.find();
140 | cachePosition = base;
141 | }
142 | // Parse: Get identifier field from parse object
143 | ParseObject photo = cache.get(index % ITEM_PER_FETCH);
144 | String identifier = photo.getString(Constants.PARSE_CLOUDINARY_FIELD);
145 | L.d("Returning identifier: %s for index %d", identifier, index);
146 | return identifier;
147 | }
148 |
149 | private String getUrl(int index) throws ParseException {
150 | String identifier = getIdentifier(index);
151 | // Cloudinary: generate a URL reflecting the thumbnail transformation on the given identifier.
152 | String url = cloudinary.url().fromIdentifier(identifier).transformation(thumbnailTransformation).generate();
153 | return url;
154 | }
155 |
156 | private void clearCache() {
157 | L.d("Clearing cache. Cache policy: %s", query.getCachePolicy().toString());
158 | cache = null;
159 | ParseQuery.clearAllCachedResults();
160 | createQuery();
161 | query.setCachePolicy(CachePolicy.NETWORK_ONLY);
162 | notifyDataSetChanged();
163 | }
164 |
165 | @Override
166 | public int getCount() {
167 | try {
168 | int count;
169 | count = query.count();
170 | return count;
171 | } catch (ParseException e) {
172 | e.printStackTrace();
173 | throw new RuntimeException("Can't query object count");
174 | }
175 | }
176 |
177 | @Override
178 | public Object getItem(int position) {
179 | return null;
180 | }
181 |
182 | @Override
183 | public long getItemId(int position) {
184 | return position;
185 | }
186 |
187 | @Override
188 | public View getView(int position, View convertView, ViewGroup parent) {
189 | final ImageView imageView;
190 | if (convertView == null) {
191 | imageView = (ImageView) getLayoutInflater().inflate(R.layout.item_grid_image, parent, false);
192 | } else {
193 | imageView = (ImageView) convertView;
194 | }
195 |
196 | try {
197 | imageLoader.displayImage(getUrl(position), imageView);
198 | } catch (ParseException e) {
199 | L.e(e, "Error getting identifier");
200 | errorMessage("Error getting identifier for image to show in list: " + e.toString());
201 | }
202 |
203 | return imageView;
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.annotation.TargetApi;
6 | import android.app.Activity;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.os.AsyncTask;
10 | import android.os.Build;
11 | import android.os.Bundle;
12 | import android.support.v4.app.NavUtils;
13 | import android.text.TextUtils;
14 | import android.view.KeyEvent;
15 | import android.view.MenuItem;
16 | import android.view.View;
17 | import android.view.inputmethod.EditorInfo;
18 | import android.widget.EditText;
19 | import android.widget.ImageView;
20 | import android.widget.TextView;
21 |
22 | import com.cloudinary.Cloudinary;
23 | import com.cloudinary.Transformation;
24 | import com.parse.ParseException;
25 | import com.parse.ParseQuery;
26 | import com.parse.ParseUser;
27 |
28 | /**
29 | * Activity which displays a login screen to the user, offering registration as
30 | * well.
31 | */
32 | public class LoginActivity extends Activity {
33 | /**
34 | * Keep track of the login task to ensure we can cancel it if requested.
35 | */
36 | private UserLoginTask mAuthTask = null;
37 |
38 | private Context mContext = this;
39 |
40 | // Values for user and password at the time of the login attempt.
41 | private String mUser;
42 | private String mPassword;
43 |
44 | // UI references.
45 | private EditText mUserView;
46 | private EditText mPasswordView;
47 | private View mLoginFormView;
48 | private View mLoginStatusView;
49 | private TextView mLoginStatusMessageView;
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 |
55 | setContentView(R.layout.activity_login);
56 | setupActionBar();
57 |
58 | setupImage();
59 |
60 | // Set up the login form.
61 | mUserView = (EditText) findViewById(R.id.user);
62 | mUserView.setText(mUser);
63 |
64 | mPasswordView = (EditText) findViewById(R.id.password);
65 | mPasswordView
66 | .setOnEditorActionListener(new TextView.OnEditorActionListener() {
67 | @Override
68 | public boolean onEditorAction(TextView textView, int id,
69 | KeyEvent keyEvent) {
70 | if (id == R.id.login || id == EditorInfo.IME_NULL) {
71 | attemptLogin();
72 | return true;
73 | }
74 | return false;
75 | }
76 | });
77 |
78 | mLoginFormView = findViewById(R.id.login_form);
79 | mLoginStatusView = findViewById(R.id.login_status);
80 | mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message);
81 |
82 | findViewById(R.id.sign_in_button).setOnClickListener(
83 | new View.OnClickListener() {
84 | @Override
85 | public void onClick(View view) {
86 | attemptLogin();
87 | }
88 | });
89 | }
90 |
91 | void setupImage() {
92 | Cloudinary cloudinary = PhotoAlbumApplication.getInstance(this).getCloudinary();
93 | String url = cloudinary.url().type("facebook").transformation(
94 | new Transformation().height(95).width(95).crop("thumb").gravity("face").effect("sepia").radius(20)
95 | .chain().angle(10)
96 | ).format("png").generate("officialchucknorrispage");
97 | new DownloadImageTask((ImageView) findViewById(R.id.logo))
98 | .execute(url);
99 | }
100 |
101 | /**
102 | * Set up the {@link android.app.ActionBar}, if the API is available.
103 | */
104 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
105 | private void setupActionBar() {
106 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
107 | // Show the Up button in the action bar.
108 | getActionBar().setDisplayHomeAsUpEnabled(true);
109 | }
110 | }
111 |
112 | @Override
113 | public boolean onOptionsItemSelected(MenuItem item) {
114 | switch (item.getItemId()) {
115 | case android.R.id.home:
116 | // This ID represents the Home or Up button. In the case of this
117 | // activity, the Up button is shown. Use NavUtils to allow users
118 | // to navigate up one level in the application structure. For
119 | // more details, see the Navigation pattern on Android Design:
120 | //
121 | // http://developer.android.com/design/patterns/navigation.html#up-vs-back
122 | //
123 | NavUtils.navigateUpFromSameTask(this);
124 | return true;
125 | }
126 | return super.onOptionsItemSelected(item);
127 | }
128 |
129 | /**
130 | * Attempts to sign in or register the account specified by the login form.
131 | * If there are form errors (invalid user, missing fields, etc.), the
132 | * errors are presented and no actual login attempt is made.
133 | */
134 | public void attemptLogin() {
135 | if (mAuthTask != null) {
136 | return;
137 | }
138 |
139 | // Reset errors.
140 | mUserView.setError(null);
141 | mPasswordView.setError(null);
142 |
143 | // Store values at the time of the login attempt.
144 | mUser = mUserView.getText().toString();
145 | mPassword = mPasswordView.getText().toString();
146 |
147 | boolean cancel = false;
148 | View focusView = null;
149 |
150 | // Check for a valid password.
151 | if (TextUtils.isEmpty(mPassword)) {
152 | mPasswordView.setError(getString(R.string.error_field_required));
153 | focusView = mPasswordView;
154 | cancel = true;
155 | } else if (mPassword.length() < 4) {
156 | mPasswordView.setError(getString(R.string.error_invalid_password));
157 | focusView = mPasswordView;
158 | cancel = true;
159 | }
160 |
161 | // Check for a valid user
162 | if (TextUtils.isEmpty(mUser)) {
163 | mUserView.setError(getString(R.string.error_field_required));
164 | focusView = mUserView;
165 | cancel = true;
166 | }
167 |
168 | if (cancel) {
169 | // There was an error; don't attempt login and focus the first
170 | // form field with an error.
171 | focusView.requestFocus();
172 | } else {
173 | // Show a progress spinner, and kick off a background task to
174 | // perform the user login attempt.
175 | mLoginStatusMessageView.setText(R.string.login_progress_signing_in);
176 | showProgress(true);
177 | mAuthTask = new UserLoginTask();
178 | mAuthTask.execute((Void) null);
179 | }
180 | }
181 |
182 | /**
183 | * Shows the progress UI and hides the login form.
184 | */
185 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
186 | private void showProgress(final boolean show) {
187 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
188 | // for very easy animations. If available, use these APIs to fade-in
189 | // the progress spinner.
190 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
191 | int shortAnimTime = getResources().getInteger(
192 | android.R.integer.config_shortAnimTime);
193 |
194 | mLoginStatusView.setVisibility(View.VISIBLE);
195 | mLoginStatusView.animate().setDuration(shortAnimTime)
196 | .alpha(show ? 1 : 0)
197 | .setListener(new AnimatorListenerAdapter() {
198 | @Override
199 | public void onAnimationEnd(Animator animation) {
200 | mLoginStatusView.setVisibility(show ? View.VISIBLE
201 | : View.GONE);
202 | }
203 | });
204 |
205 | mLoginFormView.setVisibility(View.VISIBLE);
206 | mLoginFormView.animate().setDuration(shortAnimTime)
207 | .alpha(show ? 0 : 1)
208 | .setListener(new AnimatorListenerAdapter() {
209 | @Override
210 | public void onAnimationEnd(Animator animation) {
211 | mLoginFormView.setVisibility(show ? View.GONE
212 | : View.VISIBLE);
213 | }
214 | });
215 | } else {
216 | // The ViewPropertyAnimator APIs are not available, so simply show
217 | // and hide the relevant UI components.
218 | mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
219 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
220 | }
221 | }
222 |
223 | /**
224 | * Represents an asynchronous login/registration task used to authenticate
225 | * the user.
226 | */
227 | public class UserLoginTask extends AsyncTask {
228 | @Override
229 | protected Boolean doInBackground(Void... params) {
230 | ParseUser user;
231 | try {
232 | // Parse: Look up given user
233 | L.d("UserLoginTask::doInBackground - checking if user '%s' exists", mUser);
234 | ParseQuery query = ParseQuery.getQuery(ParseUser.class).whereEqualTo("username", mUser);
235 | if (query.count() == 0) {
236 | L.i("UserLoginTask::doInBackground - doesn't exist - signing up");
237 | // Parse: User doesn't exist - create
238 | user = new ParseUser();
239 | user.setUsername(mUser);
240 | user.setPassword(mPassword);
241 | user.signUp();
242 | } else {
243 | L.d("UserLoginTask::doInBackground - exists - verifying password");
244 | // Parse: User exists - verify password
245 | ParseUser.logIn(mUser, mPassword);
246 | }
247 | L.d("UserLoginTask::doInBackground - done!");
248 |
249 | return true;
250 | } catch (ParseException e) {
251 | L.e(e, "Authentication error");
252 | return false;
253 | }
254 | }
255 |
256 | @Override
257 | protected void onPostExecute(final Boolean success) {
258 | mAuthTask = null;
259 | showProgress(false);
260 |
261 | if (success) {
262 | Intent intent = new Intent(mContext, ListPhotosActivity.class);
263 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
264 | startActivity(intent);
265 | } else {
266 | mPasswordView
267 | .setError(getString(R.string.error_incorrect_password));
268 | mPasswordView.requestFocus();
269 | }
270 | }
271 |
272 | @Override
273 | protected void onCancelled() {
274 | mAuthTask = null;
275 | showProgress(false);
276 | }
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/PhotoAlbumApplication.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import static com.cloudinary.photoalbum.Constants.TAG;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.content.pm.PackageManager.NameNotFoundException;
8 | import android.os.Bundle;
9 |
10 | import com.cloudinary.Cloudinary;
11 | import com.cloudinary.android.Utils;
12 | import com.nostra13.universalimageloader.core.DisplayImageOptions;
13 | import com.nostra13.universalimageloader.core.ImageLoader;
14 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
15 | import com.parse.Parse;
16 | import com.parse.ParseACL;
17 |
18 | public class PhotoAlbumApplication extends Application {
19 | private Cloudinary cloudinary;
20 |
21 | /**
22 | * @return An initialized Cloudinary instance
23 | */
24 | public Cloudinary getCloudinary() {
25 | return cloudinary;
26 | }
27 |
28 | /**
29 | * Provides access to the singleton and the getCloudinary method
30 | * @param context Android Application context
31 | * @return instance of the Application singleton.
32 | */
33 | public static PhotoAlbumApplication getInstance(Context context) {
34 | return (PhotoAlbumApplication)context.getApplicationContext();
35 | }
36 |
37 | /**
38 | * Initializes UIL, Parse and Cloudinary upon creation of Application.
39 | */
40 | @Override
41 | public void onCreate() {
42 | L.setTag(TAG);
43 | initUIL();
44 | initParse();
45 | initCloudinary();
46 | }
47 |
48 | private void initUIL() {
49 | DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
50 | .cacheInMemory(true)
51 | .cacheOnDisk(true)
52 | .build();
53 | ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
54 | .defaultDisplayImageOptions(defaultOptions)
55 | .build();
56 | ImageLoader.getInstance().init(config);
57 | L.i("Universal Image Loader initialized");
58 | }
59 |
60 | private void initParse() {
61 | String appId = null;
62 | String clientKey = null;
63 |
64 | try {
65 | Bundle bundle = getPackageManager()
66 | .getApplicationInfo( getPackageName(), PackageManager.GET_META_DATA)
67 | .metaData;
68 | appId = bundle.getString("PARSE_APPLICATION_ID");
69 | clientKey = bundle.getString("PARSE_CLIENT_KEY");
70 | } catch (NameNotFoundException e) {
71 | // fall-thru
72 | } catch (NullPointerException e) {
73 | // fall-thru
74 | }
75 | if (appId == null || clientKey == null) {
76 | throw new RuntimeException("Couldn't load Parse meta-data params from manifest");
77 | }
78 |
79 |
80 | /// Parse - Initializes with appId and clientKey from manifest
81 | Parse.initialize(this, appId, clientKey);
82 |
83 | ParseACL defaultACL = new ParseACL();
84 | ParseACL.setDefaultACL(defaultACL, true);
85 | L.i("Parse initialized");
86 | }
87 |
88 | private void initCloudinary() {
89 | // Cloudinary: creating a cloudinary instance using meta-data from manifest
90 |
91 | cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(this));
92 | L.i("Cloudinary initialized");
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/ShowPhotoActivity.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Locale;
5 |
6 | import android.content.Context;
7 | import android.os.Bundle;
8 | import android.support.v4.app.Fragment;
9 | import android.support.v4.app.FragmentActivity;
10 | import android.support.v4.app.FragmentManager;
11 | import android.support.v4.app.FragmentPagerAdapter;
12 | import android.support.v4.view.ViewPager;
13 | import android.util.Pair;
14 | import android.view.LayoutInflater;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.widget.ImageView;
18 |
19 | import com.cloudinary.Cloudinary;
20 | import com.cloudinary.Transformation;
21 | import com.nostra13.universalimageloader.core.ImageLoader;
22 |
23 | public class ShowPhotoActivity extends FragmentActivity {
24 | /**
25 | * The {@link android.support.v4.view.PagerAdapter} that will provide
26 | * fragments for each of the sections. We use a
27 | * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
28 | * will keep every loaded fragment in memory. If this becomes too memory
29 | * intensive, it may be best to switch to a
30 | * {@link android.support.v4.app.FragmentStatePagerAdapter}.
31 | */
32 | SectionsPagerAdapter mSectionsPagerAdapter;
33 |
34 | /**
35 | * The {@link ViewPager} that will host the section contents.
36 | */
37 | ViewPager mViewPager;
38 |
39 | Context context;
40 | String cloudinaryIdentifier;
41 |
42 | /**
43 | * Cloudinary: List of transformations and names to display
44 | */
45 | @SuppressWarnings("serial")
46 | static ArrayList> transformations = new ArrayList>() {{
47 | add(new Pair("Original", new Transformation()));
48 | add(new Pair("Round fill", new Transformation().width(400).height(700).crop("fill").radius(10)));
49 | add(new Pair("Scale", new Transformation().width(400).height(700).crop("scale")));
50 | add(new Pair("Fit", new Transformation().width(400).height(700).crop("fit")));
51 | add(new Pair("Thumb + face", new Transformation().width(400).height(700).crop("thumb").gravity("face")));
52 | add(new Pair("Shabang", new Transformation().width(400).height(700).crop("fill").gravity("north").chain().angle(20).chain().effect("sepia")));
53 | }};
54 |
55 | @Override
56 | protected void onCreate(Bundle savedInstanceState) {
57 | super.onCreate(savedInstanceState);
58 | setContentView(R.layout.activity_show_photo);
59 |
60 | // Create the adapter that will return a fragment for each of the three
61 | // primary sections of the app.
62 | mSectionsPagerAdapter = new SectionsPagerAdapter(
63 | getSupportFragmentManager());
64 |
65 | // Set up the ViewPager with the sections adapter.
66 | mViewPager = (ViewPager) findViewById(R.id.pager);
67 | mViewPager.setAdapter(mSectionsPagerAdapter);
68 |
69 | Bundle bundle = getIntent().getExtras();
70 | cloudinaryIdentifier = bundle.getString(Constants.EXTRA_PHOTO);
71 |
72 | context = this;
73 | }
74 |
75 | /**
76 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
77 | * one of the sections/tabs/pages.
78 | */
79 | public class SectionsPagerAdapter extends FragmentPagerAdapter {
80 |
81 | public SectionsPagerAdapter(FragmentManager fm) {
82 | super(fm);
83 | }
84 |
85 | @Override
86 | public Fragment getItem(int position) {
87 | // getItem is called to instantiate the fragment for the given page.
88 | // Return a DummySectionFragment (defined as a static inner class
89 | // below) with the page number as its lone argument.
90 | Fragment fragment = new ImageByURLSectionFragment();
91 | Bundle args = new Bundle();
92 |
93 | Cloudinary cloudinary = PhotoAlbumApplication.getInstance(context).getCloudinary();
94 | Pair transformation = transformations.get(position);
95 | // Cloudinary: generate a URL reflecting the given transformation on the given identifier.
96 | String url = cloudinary.url().fromIdentifier(cloudinaryIdentifier).transformation(transformation.second).generate();
97 | args.putString(ImageByURLSectionFragment.ARG_URL, url);
98 | fragment.setArguments(args);
99 | return fragment;
100 | }
101 |
102 | @Override
103 | public int getCount() {
104 | return transformations.size();
105 | }
106 |
107 | @Override
108 | public CharSequence getPageTitle(int position) {
109 | Pair transformation = transformations.get(position);
110 | Locale l = Locale.getDefault();
111 | return transformation.first.toUpperCase(l);
112 | }
113 | }
114 |
115 | /**
116 | * A fragment representing a section of the app
117 | * displays an image
118 | */
119 | public static class ImageByURLSectionFragment extends Fragment {
120 | /**
121 | * The fragment argument representing the section number for this
122 | * fragment.
123 | */
124 | public static final String ARG_URL = "url";
125 |
126 | public ImageByURLSectionFragment() {
127 | }
128 |
129 | @Override
130 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
131 | Bundle savedInstanceState) {
132 | View rootView = inflater.inflate(
133 | R.layout.fragment_show_photo, container, false);
134 |
135 | ImageView imageView = (ImageView) rootView.findViewById(R.id.image);
136 | ImageLoader.getInstance().displayImage(getArguments().getString(ARG_URL), imageView);
137 |
138 | return rootView;
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/SplashScreenActivity.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 |
8 | /**
9 | * An example full-screen activity that shows and hides the system UI (i.e.
10 | * status bar and navigation/system bar) with user interaction.
11 | *
12 | * @see SystemUiHider
13 | */
14 | public class SplashScreenActivity extends Activity {
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | L.d("SplashScreen - created");
19 |
20 | setContentView(R.layout.activity_splash_screen);
21 | scheduleRedirect();
22 | }
23 |
24 | private void scheduleRedirect() {
25 | // Calls login activity after splash screen timeout
26 | final Activity current = this;
27 | new Handler().postDelayed(new Runnable() {
28 | public void run() {
29 | Intent intent = new Intent(current, LoginActivity.class);
30 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
31 | startActivity(intent);
32 | }
33 | }, Constants.SPLASH_SCREEN_TIMEOUT);
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cloudinary/photoalbum/UploadPhotoActivity.java:
--------------------------------------------------------------------------------
1 | package com.cloudinary.photoalbum;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import android.annotation.TargetApi;
9 | import android.app.Activity;
10 | import android.app.AlertDialog;
11 | import android.app.ProgressDialog;
12 | import android.content.DialogInterface;
13 | import android.content.Intent;
14 | import android.database.Cursor;
15 | import android.net.Uri;
16 | import android.os.AsyncTask;
17 | import android.os.Build;
18 | import android.os.Bundle;
19 | import android.provider.MediaStore;
20 | import android.support.v4.app.NavUtils;
21 | import android.view.Menu;
22 | import android.view.MenuItem;
23 |
24 | import com.cloudinary.Cloudinary;
25 | import com.parse.ParseCloud;
26 | import com.parse.ParseException;
27 | import com.parse.ParseObject;
28 |
29 | public class UploadPhotoActivity extends Activity {
30 | private final static int SELECT_PICTURE = 1;
31 | private final Activity current = this;
32 | private ProgressDialog dialog = null;
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 |
38 | Intent intent = new Intent();
39 | intent.setType("image/*");
40 | intent.setAction(Intent.ACTION_GET_CONTENT);
41 | startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE);
42 | }
43 |
44 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
45 | if (requestCode == SELECT_PICTURE) {
46 | if (resultCode == RESULT_OK) {
47 | setDefaultLayout();
48 | Uri selectedImageUri = data.getData();
49 | L.d("Uploading file from URI: %s", selectedImageUri.getPath());
50 | String[] filePathColumn = {MediaStore.Images.Media.DATA};
51 |
52 | Cursor cursor = getContentResolver().query(
53 | selectedImageUri, filePathColumn, null, null, null);
54 | cursor.moveToFirst();
55 |
56 | int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
57 | String filePath = cursor.getString(columnIndex);
58 | cursor.close();
59 | L.d("Uploading file: %s", filePath);
60 | startUpload(filePath);
61 | }
62 | }
63 | }
64 |
65 | private void setDefaultLayout() {
66 | setContentView(R.layout.activity_upload_photo);
67 | // Show the Up button in the action bar.
68 | setupActionBar();
69 | }
70 |
71 | private void startUpload(String filePath) {
72 | AsyncTask task = new AsyncTask() {
73 | protected String doInBackground(String... paths) {
74 | L.d("Running upload task");
75 |
76 | // sign request
77 | Map uploadParams;
78 | try {
79 | // Parse+Cloudinary: retrieves a Cloudinary signature and upload params using the Parse cloud function.
80 | // see https://github.com/cloudinary/cloudinary_parse
81 | HashMap args = new HashMap();
82 | uploadParams = ParseCloud.callFunction(Constants.PARSE_SIGN_CLOUD_FUNCTION ,args);
83 | L.i("Signed request: %s", uploadParams.toString());
84 | } catch (ParseException e) {
85 | L.e(e, "Error signing request");
86 | return "Error signing request: " + e.toString();
87 | }
88 |
89 | // Upload to cloudinary
90 | Cloudinary cloudinary = PhotoAlbumApplication.getInstance(current).getCloudinary();
91 | File file = new File(paths[0]);
92 | @SuppressWarnings("rawtypes")
93 | Map cloudinaryResult;
94 | try {
95 | // Cloudinary: Upload file using the retrieved signature and upload params
96 | cloudinaryResult = cloudinary.uploader().upload(file, uploadParams);
97 | L.i("Uploaded file: %s", cloudinaryResult.toString());
98 | } catch (RuntimeException e) {
99 | L.e(e, "Error uploading file");
100 | return "Error uploading file: " + e.toString();
101 | } catch (IOException e) {
102 | L.e(e, "Error uploading file");
103 | return "Error uploading file: " + e.toString();
104 | }
105 |
106 | // update parse
107 | ParseObject photo = new ParseObject("Photo");
108 | try {
109 | // Parse+Cloudinary: Save a reference to the uploaded image in Parse backend. The
110 | // field may be verified using the beforeSave filter demonstrated in:
111 | // https://github.com/cloudinary/cloudinary_parse
112 | photo.put(Constants.PARSE_CLOUDINARY_FIELD, cloudinary.signedPreloadedImage(cloudinaryResult));
113 | photo.save();
114 | L.i("Saved object");
115 | } catch (Exception e) {
116 | L.e(e, "Error saving object");
117 | return "Error saving object: " + e.toString();
118 | }
119 | return null;
120 | }
121 |
122 | protected void onPostExecute(String error) {
123 | if (dialog != null) {
124 | dialog.dismiss();
125 | dialog = null;
126 | }
127 | if (error == null) {
128 | setResult(RESULT_OK);
129 | finish();
130 | } else {
131 | new AlertDialog.Builder(current)
132 | .setTitle("Error")
133 | .setMessage(error)
134 | .setPositiveButton("OK",new DialogInterface.OnClickListener() {
135 | public void onClick(DialogInterface dialog, int whichButton) {
136 | finish();
137 | }
138 | })
139 | .setCancelable(true)
140 | .create().show();
141 | }
142 | }
143 | };
144 | dialog = ProgressDialog.show(this, "Uploading", "Uploading image");
145 | task.execute(filePath);
146 | }
147 |
148 | /**
149 | * Set up the {@link android.app.ActionBar}, if the API is available.
150 | */
151 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
152 | private void setupActionBar() {
153 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
154 | getActionBar().setDisplayHomeAsUpEnabled(true);
155 | }
156 | }
157 |
158 | @Override
159 | public boolean onCreateOptionsMenu(Menu menu) {
160 | // Inflate the menu; this adds items to the action bar if it is present.
161 | getMenuInflater().inflate(R.menu.upload_photo, menu);
162 | return true;
163 | }
164 |
165 | @Override
166 | public boolean onOptionsItemSelected(MenuItem item) {
167 | switch (item.getItemId()) {
168 | case android.R.id.home:
169 | // This ID represents the Home or Up button. In the case of this
170 | // activity, the Up button is shown. Use NavUtils to allow users
171 | // to navigate up one level in the application structure. For
172 | // more details, see the Navigation pattern on Android Design:
173 | //
174 | // http://developer.android.com/design/patterns/navigation.html#up-vs-back
175 | //
176 | NavUtils.navigateUpFromSameTask(this);
177 | return true;
178 | }
179 | return super.onOptionsItemSelected(item);
180 | }
181 |
182 | }
183 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/cloudinary_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary_android_parse_sample/8f0e62bf92186e3451e62de9f61388e0a36186fb/app/src/main/res/drawable-xxhdpi/cloudinary_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/cloudinary_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary_android_parse_sample/8f0e62bf92186e3451e62de9f61388e0a36186fb/app/src/main/res/drawable-xxhdpi/cloudinary_title.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_list_photos.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
15 |
16 |
21 |
22 |
30 |
31 |
32 |
33 |
34 |
38 |
39 |
42 |
43 |
51 |
52 |
63 |
64 |
73 |
74 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_show_photo.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
12 |
13 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash_screen.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_upload_photo.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_show_photo.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_grid_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/signin.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
49 |
50 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/list_photos.xml:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/upload_photo.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-large/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #66000000
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Photo Album
5 | Username:
6 | Password:
7 | Sign in
8 | ListPhotosActivity
9 | Upload
10 | Refresh
11 | Logout
12 | UploadPhotoActivity
13 | Settings
14 | Image
15 | ShowPhotoActivity
16 | Logo
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings_activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sign in
4 |
5 |
6 | Username
7 | Password
8 | Sign in or register
9 | Sign in
10 | Signing in…
11 | This password is too short
12 | This password is incorrect
13 | This field is required
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
15 |
16 |
17 |
20 |
21 |
28 |
29 |
30 |
31 |
36 |
37 |
--------------------------------------------------------------------------------
/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:1.0.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 |
--------------------------------------------------------------------------------
/cloudinary_android_parse_sample.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudinary/cloudinary_android_parse_sample/8f0e62bf92186e3451e62de9f61388e0a36186fb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
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.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/third_party_licenses.txt:
--------------------------------------------------------------------------------
1 | THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE PARSE PRODUCT.
2 |
3 | -----
4 |
5 | The following software may be included in this product: Apache Jakarta Commons Codec. This software contains the following license and notice below:
6 |
7 |
8 | Apache License
9 | Version 2.0, January 2004
10 | http://www.apache.org/licenses/
11 |
12 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
13 |
14 | 1. Definitions.
15 |
16 | "License" shall mean the terms and conditions for use, reproduction,
17 | and distribution as defined by Sections 1 through 9 of this document.
18 |
19 | "Licensor" shall mean the copyright owner or entity authorized by
20 | the copyright owner that is granting the License.
21 |
22 | "Legal Entity" shall mean the union of the acting entity and all
23 | other entities that control, are controlled by, or are under common
24 | control with that entity. For the purposes of this definition,
25 | "control" means (i) the power, direct or indirect, to cause the
26 | direction or management of such entity, whether by contract or
27 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
28 | outstanding shares, or (iii) beneficial ownership of such entity.
29 |
30 | "You" (or "Your") shall mean an individual or Legal Entity
31 | exercising permissions granted by this License.
32 |
33 | "Source" form shall mean the preferred form for making modifications,
34 | including but not limited to software source code, documentation
35 | source, and configuration files.
36 |
37 | "Object" form shall mean any form resulting from mechanical
38 | transformation or translation of a Source form, including but
39 | not limited to compiled object code, generated documentation,
40 | and conversions to other media types.
41 |
42 | "Work" shall mean the work of authorship, whether in Source or
43 | Object form, made available under the License, as indicated by a
44 | copyright notice that is included in or attached to the work
45 | (an example is provided in the Appendix below).
46 |
47 | "Derivative Works" shall mean any work, whether in Source or Object
48 | form, that is based on (or derived from) the Work and for which the
49 | editorial revisions, annotations, elaborations, or other modifications
50 | represent, as a whole, an original work of authorship. For the purposes
51 | of this License, Derivative Works shall not include works that remain
52 | separable from, or merely link (or bind by name) to the interfaces of,
53 | the Work and Derivative Works thereof.
54 |
55 | "Contribution" shall mean any work of authorship, including
56 | the original version of the Work and any modifications or additions
57 | to that Work or Derivative Works thereof, that is intentionally
58 | submitted to Licensor for inclusion in the Work by the copyright owner
59 | or by an individual or Legal Entity authorized to submit on behalf of
60 | the copyright owner. For the purposes of this definition, "submitted"
61 | means any form of electronic, verbal, or written communication sent
62 | to the Licensor or its representatives, including but not limited to
63 | communication on electronic mailing lists, source code control systems,
64 | and issue tracking systems that are managed by, or on behalf of, the
65 | Licensor for the purpose of discussing and improving the Work, but
66 | excluding communication that is conspicuously marked or otherwise
67 | designated in writing by the copyright owner as "Not a Contribution."
68 |
69 | "Contributor" shall mean Licensor and any individual or Legal Entity
70 | on behalf of whom a Contribution has been received by Licensor and
71 | subsequently incorporated within the Work.
72 |
73 | 2. Grant of Copyright License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | copyright license to reproduce, prepare Derivative Works of,
77 | publicly display, publicly perform, sublicense, and distribute the
78 | Work and such Derivative Works in Source or Object form.
79 |
80 | 3. Grant of Patent License. Subject to the terms and conditions of
81 | this License, each Contributor hereby grants to You a perpetual,
82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
83 | (except as stated in this section) patent license to make, have made,
84 | use, offer to sell, sell, import, and otherwise transfer the Work,
85 | where such license applies only to those patent claims licensable
86 | by such Contributor that are necessarily infringed by their
87 | Contribution(s) alone or by combination of their Contribution(s)
88 | with the Work to which such Contribution(s) was submitted. If You
89 | institute patent litigation against any entity (including a
90 | cross-claim or counterclaim in a lawsuit) alleging that the Work
91 | or a Contribution incorporated within the Work constitutes direct
92 | or contributory patent infringement, then any patent licenses
93 | granted to You under this License for that Work shall terminate
94 | as of the date such litigation is filed.
95 |
96 | 4. Redistribution. You may reproduce and distribute copies of the
97 | Work or Derivative Works thereof in any medium, with or without
98 | modifications, and in Source or Object form, provided that You
99 | meet the following conditions:
100 |
101 | (a) You must give any other recipients of the Work or
102 | Derivative Works a copy of this License; and
103 |
104 | (b) You must cause any modified files to carry prominent notices
105 | stating that You changed the files; and
106 |
107 | (c) You must retain, in the Source form of any Derivative Works
108 | that You distribute, all copyright, patent, trademark, and
109 | attribution notices from the Source form of the Work,
110 | excluding those notices that do not pertain to any part of
111 | the Derivative Works; and
112 |
113 | (d) If the Work includes a "NOTICE" text file as part of its
114 | distribution, then any Derivative Works that You distribute must
115 | include a readable copy of the attribution notices contained
116 | within such NOTICE file, excluding those notices that do not
117 | pertain to any part of the Derivative Works, in at least one
118 | of the following places: within a NOTICE text file distributed
119 | as part of the Derivative Works; within the Source form or
120 | documentation, if provided along with the Derivative Works; or,
121 | within a display generated by the Derivative Works, if and
122 | wherever such third-party notices normally appear. The contents
123 | of the NOTICE file are for informational purposes only and
124 | do not modify the License. You may add Your own attribution
125 | notices within Derivative Works that You distribute, alongside
126 | or as an addendum to the NOTICE text from the Work, provided
127 | that such additional attribution notices cannot be construed
128 | as modifying the License.
129 |
130 | You may add Your own copyright statement to Your modifications and
131 | may provide additional or different license terms and conditions
132 | for use, reproduction, or distribution of Your modifications, or
133 | for any such Derivative Works as a whole, provided Your use,
134 | reproduction, and distribution of the Work otherwise complies with
135 | the conditions stated in this License.
136 |
137 | 5. Submission of Contributions. Unless You explicitly state otherwise,
138 | any Contribution intentionally submitted for inclusion in the Work
139 | by You to the Licensor shall be under the terms and conditions of
140 | this License, without any additional terms or conditions.
141 | Notwithstanding the above, nothing herein shall supersede or modify
142 | the terms of any separate license agreement you may have executed
143 | with Licensor regarding such Contributions.
144 |
145 | 6. Trademarks. This License does not grant permission to use the trade
146 | names, trademarks, service marks, or product names of the Licensor,
147 | except as required for reasonable and customary use in describing the
148 | origin of the Work and reproducing the content of the NOTICE file.
149 |
150 | 7. Disclaimer of Warranty. Unless required by applicable law or
151 | agreed to in writing, Licensor provides the Work (and each
152 | Contributor provides its Contributions) on an "AS IS" BASIS,
153 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
154 | implied, including, without limitation, any warranties or conditions
155 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
156 | PARTICULAR PURPOSE. You are solely responsible for determining the
157 | appropriateness of using or redistributing the Work and assume any
158 | risks associated with Your exercise of permissions under this License.
159 |
160 | 8. Limitation of Liability. In no event and under no legal theory,
161 | whether in tort (including negligence), contract, or otherwise,
162 | unless required by applicable law (such as deliberate and grossly
163 | negligent acts) or agreed to in writing, shall any Contributor be
164 | liable to You for damages, including any direct, indirect, special,
165 | incidental, or consequential damages of any character arising as a
166 | result of this License or out of the use or inability to use the
167 | Work (including but not limited to damages for loss of goodwill,
168 | work stoppage, computer failure or malfunction, or any and all
169 | other commercial damages or losses), even if such Contributor
170 | has been advised of the possibility of such damages.
171 |
172 | 9. Accepting Warranty or Additional Liability. While redistributing
173 | the Work or Derivative Works thereof, You may choose to offer,
174 | and charge a fee for, acceptance of support, warranty, indemnity,
175 | or other liability obligations and/or rights consistent with this
176 | License. However, in accepting such obligations, You may act only
177 | on Your own behalf and on Your sole responsibility, not on behalf
178 | of any other Contributor, and only if You agree to indemnify,
179 | defend, and hold each Contributor harmless for any liability
180 | incurred by, or claims asserted against, such Contributor by reason
181 | of your accepting any such warranty or additional liability.
182 |
183 | END OF TERMS AND CONDITIONS
184 |
185 | APPENDIX: How to apply the Apache License to your work.
186 |
187 | To apply the Apache License to your work, attach the following
188 | boilerplate notice, with the fields enclosed by brackets "[]"
189 | replaced with your own identifying information. (Don't include
190 | the brackets!) The text should be enclosed in the appropriate
191 | comment syntax for the file format. We also recommend that a
192 | file or class name and description of purpose be included on the
193 | same "printed page" as the copyright notice for easier
194 | identification within third-party archives.
195 |
196 | Copyright [yyyy] [name of copyright owner]
197 |
198 | Licensed under the Apache License, Version 2.0 (the "License");
199 | you may not use this file except in compliance with the License.
200 | You may obtain a copy of the License at
201 |
202 | http://www.apache.org/licenses/LICENSE-2.0
203 |
204 | Unless required by applicable law or agreed to in writing, software
205 | distributed under the License is distributed on an "AS IS" BASIS,
206 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
207 | See the License for the specific language governing permissions and
208 | limitations under the License.
209 |
210 | -----
211 |
212 | The following software may be included in this product: oauth-signpost. This software contains the following license and notice below:
213 |
214 |
215 | Apache License
216 | Version 2.0, January 2004
217 | http://www.apache.org/licenses/
218 |
219 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
220 |
221 | 1. Definitions.
222 |
223 | "License" shall mean the terms and conditions for use, reproduction,
224 | and distribution as defined by Sections 1 through 9 of this document.
225 |
226 | "Licensor" shall mean the copyright owner or entity authorized by
227 | the copyright owner that is granting the License.
228 |
229 | "Legal Entity" shall mean the union of the acting entity and all
230 | other entities that control, are controlled by, or are under common
231 | control with that entity. For the purposes of this definition,
232 | "control" means (i) the power, direct or indirect, to cause the
233 | direction or management of such entity, whether by contract or
234 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
235 | outstanding shares, or (iii) beneficial ownership of such entity.
236 |
237 | "You" (or "Your") shall mean an individual or Legal Entity
238 | exercising permissions granted by this License.
239 |
240 | "Source" form shall mean the preferred form for making modifications,
241 | including but not limited to software source code, documentation
242 | source, and configuration files.
243 |
244 | "Object" form shall mean any form resulting from mechanical
245 | transformation or translation of a Source form, including but
246 | not limited to compiled object code, generated documentation,
247 | and conversions to other media types.
248 |
249 | "Work" shall mean the work of authorship, whether in Source or
250 | Object form, made available under the License, as indicated by a
251 | copyright notice that is included in or attached to the work
252 | (an example is provided in the Appendix below).
253 |
254 | "Derivative Works" shall mean any work, whether in Source or Object
255 | form, that is based on (or derived from) the Work and for which the
256 | editorial revisions, annotations, elaborations, or other modifications
257 | represent, as a whole, an original work of authorship. For the purposes
258 | of this License, Derivative Works shall not include works that remain
259 | separable from, or merely link (or bind by name) to the interfaces of,
260 | the Work and Derivative Works thereof.
261 |
262 | "Contribution" shall mean any work of authorship, including
263 | the original version of the Work and any modifications or additions
264 | to that Work or Derivative Works thereof, that is intentionally
265 | submitted to Licensor for inclusion in the Work by the copyright owner
266 | or by an individual or Legal Entity authorized to submit on behalf of
267 | the copyright owner. For the purposes of this definition, "submitted"
268 | means any form of electronic, verbal, or written communication sent
269 | to the Licensor or its representatives, including but not limited to
270 | communication on electronic mailing lists, source code control systems,
271 | and issue tracking systems that are managed by, or on behalf of, the
272 | Licensor for the purpose of discussing and improving the Work, but
273 | excluding communication that is conspicuously marked or otherwise
274 | designated in writing by the copyright owner as "Not a Contribution."
275 |
276 | "Contributor" shall mean Licensor and any individual or Legal Entity
277 | on behalf of whom a Contribution has been received by Licensor and
278 | subsequently incorporated within the Work.
279 |
280 | 2. Grant of Copyright License. Subject to the terms and conditions of
281 | this License, each Contributor hereby grants to You a perpetual,
282 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
283 | copyright license to reproduce, prepare Derivative Works of,
284 | publicly display, publicly perform, sublicense, and distribute the
285 | Work and such Derivative Works in Source or Object form.
286 |
287 | 3. Grant of Patent License. Subject to the terms and conditions of
288 | this License, each Contributor hereby grants to You a perpetual,
289 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
290 | (except as stated in this section) patent license to make, have made,
291 | use, offer to sell, sell, import, and otherwise transfer the Work,
292 | where such license applies only to those patent claims licensable
293 | by such Contributor that are necessarily infringed by their
294 | Contribution(s) alone or by combination of their Contribution(s)
295 | with the Work to which such Contribution(s) was submitted. If You
296 | institute patent litigation against any entity (including a
297 | cross-claim or counterclaim in a lawsuit) alleging that the Work
298 | or a Contribution incorporated within the Work constitutes direct
299 | or contributory patent infringement, then any patent licenses
300 | granted to You under this License for that Work shall terminate
301 | as of the date such litigation is filed.
302 |
303 | 4. Redistribution. You may reproduce and distribute copies of the
304 | Work or Derivative Works thereof in any medium, with or without
305 | modifications, and in Source or Object form, provided that You
306 | meet the following conditions:
307 |
308 | (a) You must give any other recipients of the Work or
309 | Derivative Works a copy of this License; and
310 |
311 | (b) You must cause any modified files to carry prominent notices
312 | stating that You changed the files; and
313 |
314 | (c) You must retain, in the Source form of any Derivative Works
315 | that You distribute, all copyright, patent, trademark, and
316 | attribution notices from the Source form of the Work,
317 | excluding those notices that do not pertain to any part of
318 | the Derivative Works; and
319 |
320 | (d) If the Work includes a "NOTICE" text file as part of its
321 | distribution, then any Derivative Works that You distribute must
322 | include a readable copy of the attribution notices contained
323 | within such NOTICE file, excluding those notices that do not
324 | pertain to any part of the Derivative Works, in at least one
325 | of the following places: within a NOTICE text file distributed
326 | as part of the Derivative Works; within the Source form or
327 | documentation, if provided along with the Derivative Works; or,
328 | within a display generated by the Derivative Works, if and
329 | wherever such third-party notices normally appear. The contents
330 | of the NOTICE file are for informational purposes only and
331 | do not modify the License. You may add Your own attribution
332 | notices within Derivative Works that You distribute, alongside
333 | or as an addendum to the NOTICE text from the Work, provided
334 | that such additional attribution notices cannot be construed
335 | as modifying the License.
336 |
337 | You may add Your own copyright statement to Your modifications and
338 | may provide additional or different license terms and conditions
339 | for use, reproduction, or distribution of Your modifications, or
340 | for any such Derivative Works as a whole, provided Your use,
341 | reproduction, and distribution of the Work otherwise complies with
342 | the conditions stated in this License.
343 |
344 | 5. Submission of Contributions. Unless You explicitly state otherwise,
345 | any Contribution intentionally submitted for inclusion in the Work
346 | by You to the Licensor shall be under the terms and conditions of
347 | this License, without any additional terms or conditions.
348 | Notwithstanding the above, nothing herein shall supersede or modify
349 | the terms of any separate license agreement you may have executed
350 | with Licensor regarding such Contributions.
351 |
352 | 6. Trademarks. This License does not grant permission to use the trade
353 | names, trademarks, service marks, or product names of the Licensor,
354 | except as required for reasonable and customary use in describing the
355 | origin of the Work and reproducing the content of the NOTICE file.
356 |
357 | 7. Disclaimer of Warranty. Unless required by applicable law or
358 | agreed to in writing, Licensor provides the Work (and each
359 | Contributor provides its Contributions) on an "AS IS" BASIS,
360 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
361 | implied, including, without limitation, any warranties or conditions
362 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
363 | PARTICULAR PURPOSE. You are solely responsible for determining the
364 | appropriateness of using or redistributing the Work and assume any
365 | risks associated with Your exercise of permissions under this License.
366 |
367 | 8. Limitation of Liability. In no event and under no legal theory,
368 | whether in tort (including negligence), contract, or otherwise,
369 | unless required by applicable law (such as deliberate and grossly
370 | negligent acts) or agreed to in writing, shall any Contributor be
371 | liable to You for damages, including any direct, indirect, special,
372 | incidental, or consequential damages of any character arising as a
373 | result of this License or out of the use or inability to use the
374 | Work (including but not limited to damages for loss of goodwill,
375 | work stoppage, computer failure or malfunction, or any and all
376 | other commercial damages or losses), even if such Contributor
377 | has been advised of the possibility of such damages.
378 |
379 | 9. Accepting Warranty or Additional Liability. While redistributing
380 | the Work or Derivative Works thereof, You may choose to offer,
381 | and charge a fee for, acceptance of support, warranty, indemnity,
382 | or other liability obligations and/or rights consistent with this
383 | License. However, in accepting such obligations, You may act only
384 | on Your own behalf and on Your sole responsibility, not on behalf
385 | of any other Contributor, and only if You agree to indemnify,
386 | defend, and hold each Contributor harmless for any liability
387 | incurred by, or claims asserted against, such Contributor by reason
388 | of your accepting any such warranty or additional liability.
389 |
390 | END OF TERMS AND CONDITIONS
391 |
392 | APPENDIX: How to apply the Apache License to your work.
393 |
394 | To apply the Apache License to your work, attach the following
395 | boilerplate notice, with the fields enclosed by brackets "[]"
396 | replaced with your own identifying information. (Don't include
397 | the brackets!) The text should be enclosed in the appropriate
398 | comment syntax for the file format. We also recommend that a
399 | file or class name and description of purpose be included on the
400 | same "printed page" as the copyright notice for easier
401 | identification within third-party archives.
402 |
403 | Copyright [yyyy] [name of copyright owner]
404 |
405 | Licensed under the Apache License, Version 2.0 (the "License");
406 | you may not use this file except in compliance with the License.
407 | You may obtain a copy of the License at
408 |
409 | http://www.apache.org/licenses/LICENSE-2.0
410 |
411 | Unless required by applicable law or agreed to in writing, software
412 | distributed under the License is distributed on an "AS IS" BASIS,
413 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
414 | See the License for the specific language governing permissions and
415 | limitations under the License.
416 |
--------------------------------------------------------------------------------