Java tutorial
/* * Copyright 2015 HE5ED.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.he5ed.lib.cloudprovider.apis; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import com.he5ed.lib.cloudprovider.CloudProvider; import com.he5ed.lib.cloudprovider.R; import com.he5ed.lib.cloudprovider.auth.Authenticator; import com.he5ed.lib.cloudprovider.exceptions.RequestFailException; import com.he5ed.lib.cloudprovider.models.CFile; import com.he5ed.lib.cloudprovider.models.CFolder; import com.he5ed.lib.cloudprovider.models.User; import com.he5ed.lib.cloudprovider.utils.FilesUtils; import com.squareup.okhttp.Callback; import com.squareup.okhttp.FormEncodingBuilder; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.MultipartBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import okio.BufferedSink; /** * Amazon CloudDrive cloud service API implementation * * @hide */ public class CloudDriveApi extends BaseApi { // API server constant values public static final String AUTH_URL = "https://www.amazon.com/ap/oa"; public static final String TOKEN_URL = "https://api.amazon.com/auth/o2/token"; public static final String REVOKE_URL = null; public static final String API_BASE_URL = "https://drive.amazonaws.com/drive/v1"; public static String ROOT_ID = ""; /** * Must override with the correct values */ public static String CLIENT_ID = null; public static String CLIENT_SECRET = null; public static String REDIRECT_URL = null; // enable API public static boolean ENABLE_API = false; // class constant values public static final String NAME = "Amazon Cloud Drive"; public static final int ICON_RESOURCE = R.drawable.ic_amazon_color_24dp; public static final String TAG = "CloudDriveApi"; public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); /** * Build authorization url base on type of cloud service * * @return Uri */ public static Uri buildAuthUri(String stateString) { Uri uri = Uri.parse(AUTH_URL); return uri.buildUpon().appendQueryParameter("response_type", "code") .appendQueryParameter("client_id", CLIENT_ID).appendQueryParameter("redirect_uri", REDIRECT_URL) .appendQueryParameter("state", stateString) .appendQueryParameter("scope", "profile clouddrive:read_all clouddrive:write").build(); } /** * Get parameters to be passed to Volley request to get access token * * @param authCode code from authorization process * @return Map */ public static RequestBody getAccessTokenBody(String authCode) { return new FormEncodingBuilder().addEncoded("grant_type", "authorization_code").addEncoded("code", authCode) .addEncoded("client_id", CLIENT_ID).addEncoded("client_secret", CLIENT_SECRET) .addEncoded("redirect_uri", REDIRECT_URL).build(); } /** * Build form body to be passed to access token * * @param refreshToken code from authorization process * @return RequestBody */ public static RequestBody getRefreshTokenBody(String refreshToken) { return new FormEncodingBuilder().addEncoded("grant_type", "refresh_token") .addEncoded("refresh_token", refreshToken).addEncoded("client_id", CLIENT_ID) .addEncoded("client_secret", CLIENT_SECRET).build(); } /** * Extract access token from the JSONObject * * @param jsonObject JSONObject that contain access token * @return Map * @throws JSONException */ public static Map<String, String> extractAccessToken(JSONObject jsonObject) throws JSONException { Map<String, String> map = new HashMap<>(); map.put(Authenticator.KEY_ACCESS_TOKEN, jsonObject.getString("access_token")); map.put(Authenticator.KEY_EXPIRY, String.valueOf(jsonObject.getLong("expires_in"))); map.put(Authenticator.KEY_REFRESH_TOKEN, jsonObject.getString("refresh_token")); return map; } /** * Get current user end point url * * @return Uri */ public static Uri getEndPointUri() { Uri uri = Uri.parse(API_BASE_URL); return uri.buildUpon().appendEncodedPath("account/endpoint").build(); } /** * Get current user information uri * * @return Uri */ public static Uri getUserInfoUri() { return Uri.parse("https://api.amazon.com/user/profile"); } /** * Create request to get user endpoint * * @param accessToken access token for authorization * @return Request */ public static Request getEndPointRequest(String accessToken) { return new Request.Builder().url(getEndPointUri().toString()) .addHeader("Authorization", String.format("Bearer %s", accessToken)).build(); } /** * Create request to get user information * * @param accessToken access token for authorization * @return Request */ public static Request getUserInfoRequest(String accessToken) { return new Request.Builder().url(getUserInfoUri().toString()) .addHeader("Authorization", String.format("Bearer %s", accessToken)).build(); } /** * Extract user information from the JSONObject * * @param jsonObject JSONObject that contain user information * @return User * @throws JSONException */ public static User extractUser(JSONObject jsonObject) throws JSONException { User user = new User(); user.id = jsonObject.getString("user_id"); user.name = jsonObject.getString("name"); user.displayName = jsonObject.getString("name"); user.email = jsonObject.getString("email"); return user; } /** * Build folder from JSONObject * * @param jsonObject from http response * @return CFolder */ public static CFolder buildFolder(JSONObject jsonObject) { Map<String, Object> map = new HashMap<>(); try { map.put(CFolder.ID, jsonObject.getString("id")); map.put(CFolder.NAME, jsonObject.getString("name")); map.put(CFolder.DATE_FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ"); if (jsonObject.has("created_at")) map.put(CFolder.CREATED, jsonObject.getString("created_at")); if (jsonObject.has("modified_at")) map.put(CFolder.MODIFIED, jsonObject.getString("modified_at")); } catch (JSONException e) { e.printStackTrace(); return null; } return new CFolder(map); } /** * Build file from JSONObject * * @param jsonObject from http response * @return CFolder */ public static CFile buildFile(JSONObject jsonObject) { Map<String, Object> map = new HashMap<>(); try { map.put(CFolder.ID, jsonObject.getString("id")); map.put(CFolder.NAME, jsonObject.getString("name")); map.put(CFolder.DATE_FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ"); if (jsonObject.has("created_at")) map.put(CFolder.CREATED, jsonObject.getString("created_at")); if (jsonObject.has("modified_at")) map.put(CFolder.MODIFIED, jsonObject.getString("modified_at")); } catch (JSONException e) { e.printStackTrace(); return null; } return new CFile(map); } private Context mContext; private CloudProvider mCloudProvider; private Account mAccount; private BaseApi.OnPrepareListener mPrepareListener; private OkHttpClient mHttpClient; private String mAccessToken; private String mContentUrl; private String mMetadataUrl; /** * Constructor for Cloud Drive API * * @param account to be used must be Cloud Drive type */ public CloudDriveApi(Context context, Account account) { mContext = context; mAccount = account; mCloudProvider = CloudProvider.getInstance(mContext); mHttpClient = new OkHttpClient(); } @Override public synchronized void prepareApi(BaseApi.OnPrepareListener prepareListener) { mPrepareListener = prepareListener; AccountManager.get(mContext).getAuthToken(mAccount, CloudProvider.AUTH_TYPE, false, new AccountManagerCallback<Bundle>() { @Override public void run(AccountManagerFuture<Bundle> future) { try { mAccessToken = future.getResult().getString(AccountManager.KEY_AUTHTOKEN); validateAccessToken(); } catch (OperationCanceledException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); if (mPrepareListener != null) mPrepareListener.onPrepareFail(e); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); if (mPrepareListener != null) mPrepareListener.onPrepareFail(e); } catch (AuthenticatorException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); if (mPrepareListener != null) mPrepareListener.onPrepareFail(e); } } }, null); } /** * Ensure that the access token is still valid * Access token can be expired or revoked by user * Try to refresh the access token if it is expired */ private void validateAccessToken() { Request request = getEndPointRequest(mAccessToken); mHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { e.printStackTrace(); if (mPrepareListener != null) mPrepareListener.onPrepareFail(e); Log.e(TAG, e.getMessage()); } @Override public void onResponse(Response response) throws IOException { if (response.isSuccessful() && response.code() == 200) { try { JSONObject jsonObject = new JSONObject(response.body().string()); mContentUrl = jsonObject.getString("contentUrl"); mMetadataUrl = jsonObject.getString("metadataUrl"); if (mPrepareListener != null) mPrepareListener.onPrepareSuccessful(); } catch (JSONException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); if (mPrepareListener != null) mPrepareListener.onPrepareFail(e); } } else { switch (response.code()) { case 401: // unauthorized refreshAccessToken(); break; default: break; } Log.e(TAG, response.code() + ": " + response.body().string()); } } }); } /** * Try to get a fresh access token using the refresh token */ private void refreshAccessToken() { final String refreshToken = mCloudProvider.getUserData(mAccount, Authenticator.KEY_REFRESH_TOKEN); if (!TextUtils.isEmpty(refreshToken)) { Request request = new Request.Builder().url(TOKEN_URL).post(getRefreshTokenBody(refreshToken)).build(); mHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); resetAccount(); } @Override public void onResponse(Response response) throws IOException { if (response.isSuccessful()) { // convert string into json try { JSONObject jsonObject = new JSONObject(response.body().string()); Map<String, String> tokenInfo = extractAccessToken(jsonObject); mCloudProvider.updateAccount(mAccount, tokenInfo); mAccessToken = tokenInfo.get(Authenticator.KEY_ACCESS_TOKEN); // validate again validateAccessToken(); } catch (JSONException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } } else { Log.e(TAG, response.code() + ": " + response.body().string()); resetAccount(); } } }); } else { resetAccount(); } } /** * Remove the staled account and add a new one */ private void resetAccount() { logout(new Callback() { @Override public void onFailure(Request request, IOException e) { } @Override public void onResponse(Response response) throws IOException { } }); // use Authenticator to update account mCloudProvider.removeAccount(mAccount); mCloudProvider.addAccount(getClass().getCanonicalName(), (Activity) mContext); } @Override public void logout(@NonNull Callback callback) { RequestBody body = new FormEncodingBuilder().add("client_id", CLIENT_ID).add("client_secret", CLIENT_SECRET) .add("token", mAccessToken).build(); Request request = new Request.Builder().url(REVOKE_URL).post(body).build(); mHttpClient.newCall(request).enqueue(callback); } @Override public synchronized List<Object> exploreFolder(@NonNull CFolder folder, int offset) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } // if folder id is empty set it to root id if (TextUtils.isEmpty(folder.getId())) folder.setId(getRootId()); List<Object> list = new ArrayList<>(); String folderId = folder.getId(); Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + folderId + "/children").build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); int total = jsonObject.getInt("count"); // return null if no item found if (total == 0) return null; JSONArray entries = jsonObject.getJSONArray("data"); list.addAll(createItemList(entries)); // pagination available if (jsonObject.has("nextToken")) { list.addAll(exploreFolderContinue(folderId, jsonObject.getString("nextToken"))); } return list; } else { switch (response.code()) { case 404: // no item found throw new RequestFailException("No item found"); case 401: // unauthorized throw new RequestFailException("Unauthorized request"); default: break; } } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } return null; } /** * Get continue folder items * * @param folderId of the folder to explore * @param startToken nextToken from previous request for access more content * @return List that contains CFile and CFolder * @throws RequestFailException that content various error types */ public synchronized List<Object> exploreFolderContinue(String folderId, String startToken) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } List<Object> list = new ArrayList<>(); Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + folderId + "/children") .appendQueryParameter("startToken", startToken).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); JSONArray entries = jsonObject.getJSONArray("data"); if (entries.length() > 0) { list.addAll(createItemList(entries)); } else { // return null if no item found return null; } // pagination available if (jsonObject.has("nextToken")) { list.addAll(exploreFolderContinue(folderId, jsonObject.getString("nextToken"))); } return list; } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFolder getRoot() { // create root folder manually CFolder root = new CFolder(null); root.setId(ROOT_ID); root.setName(mContext.getString(R.string.home_folder_title)); root.setRoot(true); return root; } @Override public CFolder getFolderInfo(@NonNull String folderId) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + folderId).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); return buildFolder(jsonObject); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFolder createFolder(@NonNull String name, @Nullable CFolder parent) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/").build().toString(); // create parameter as json final JSONObject params = new JSONObject(); try { params.put("name", name); params.put("kind", "FOLDER"); ArrayList<String> parentList = new ArrayList<>(); parentList.add(parent != null ? parent.getId() : getRoot().getId()); params.put("parents", new JSONArray(parentList)); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).post(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); return buildFolder(jsonObject); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFolder renameFolder(@NonNull CFolder folder, String name) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } // exit if root or same name if (folder.isRoot() || folder.getName().equals(name)) return folder; // create parameter as json final JSONObject params = new JSONObject(); try { params.put("name", name); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + folder.getId()).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).patch(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { // return folder object return buildFolder(new JSONObject(response.body().string())); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFolder moveFolder(@NonNull CFolder folder, @Nullable CFolder parent) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } // exit if root or same name if (folder.isRoot() || parent != null && folder.getId().equals(parent.getId())) return folder; // create parameter as json final JSONObject params = new JSONObject(); try { ArrayList<String> parentList = new ArrayList<>(); parentList.add(parent != null ? parent.getId() : getRoot().getId()); params.put("parents", new JSONArray(parentList)); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + folder.getId()).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).patch(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { // return folder object return buildFolder(new JSONObject(response.body().string())); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public void deleteFolder(@NonNull CFolder folder) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("trash/" + folder.getId()).build().toString(); // create parameter as json final JSONObject params = new JSONObject(); try { params.put("kind", "FOLDER"); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).put(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Log.d(TAG, "CFolder with the id: " + folder.getName() + " deleted"); } else { throw new RequestFailException(response.message(), response.code()); } } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFile getFileInfo(@NonNull String fileId) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + fileId).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); return buildFile(jsonObject); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFile uploadFile(@NonNull File file, @Nullable CFolder parent) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mContentUrl); String url = uri.buildUpon().appendEncodedPath("nodes").build().toString(); // create parameter as json final JSONObject params = new JSONObject(); try { params.put("name", file.getName()); params.put("kind", "FILE"); ArrayList<String> parentList = new ArrayList<>(); parentList.add(parent != null ? parent.getId() : getRoot().getId()); params.put("parents", new JSONArray(parentList)); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } // create multipart body MediaType fileType = MediaType.parse(FilesUtils.getFileType(file)); RequestBody multipart = new MultipartBuilder().type(MultipartBuilder.FORM) .addFormDataPart("metadata", params.toString()) .addFormDataPart("content", file.getName(), RequestBody.create(fileType, file)).build(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).post(multipart).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); return buildFile(jsonObject); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFile updateFile(@NonNull CFile file, File content) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mContentUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + file.getId() + "/content").build().toString(); // create multipart body MediaType fileType = MediaType.parse(FilesUtils.getFileType(content)); RequestBody multipart = new MultipartBuilder().type(MultipartBuilder.FORM) .addFormDataPart("content", file.getName(), RequestBody.create(fileType, content)).build(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).put(multipart).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); return buildFile(jsonObject); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFile renameFile(@NonNull CFile file, String name) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } // exist if same filename if (file.getName().equals(name)) return file; // create parameter as json final JSONObject params = new JSONObject(); try { params.put("name", name); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + file.getId()).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).patch(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { // return file object return buildFile(new JSONObject(response.body().string())); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public CFile moveFile(@NonNull CFile file, @Nullable CFolder folder) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } // create parameter as json final JSONObject params = new JSONObject(); try { ArrayList<String> parentList = new ArrayList<>(); parentList.add(folder != null ? folder.getId() : getRoot().getId()); params.put("parents", new JSONArray(parentList)); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + file.getId()).build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).patch(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { // return file object return buildFile(new JSONObject(response.body().string())); } else { throw new RequestFailException(response.message(), response.code()); } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public File downloadFile(@NonNull CFile file, String filename) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mContentUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + file.getId() + "/content").build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { File localFile = new File(mContext.getFilesDir(), TextUtils.isEmpty(filename) ? file.getName() : filename); Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { FilesUtils.copyFile(response.body().byteStream(), new FileOutputStream(localFile)); } else { throw new RequestFailException(response.message(), response.code()); } return localFile; } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public void deleteFile(@NonNull CFile file) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("trash/" + file.getId()).build().toString(); // create parameter as json final JSONObject params = new JSONObject(); try { params.put("kind", "FOLDER"); } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } RequestBody body = new RequestBody() { @Override public MediaType contentType() { return JSON; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8(params.toString()); } }; Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).put(body).build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Log.d(TAG, "File with the id: " + file.getName() + " deleted"); } else { throw new RequestFailException(response.message(), response.code()); } } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } @Override public List<CFile> searchFile(@NonNull String keyword, CFolder folder) throws RequestFailException { List<Object> list = exploreFolder(folder, 0); List<CFile> resultList = new ArrayList<>(); // search through every item's name for keyword match in case insensitive mode for (Object item : list) { if (CFile.class.isAssignableFrom(item.getClass())) { if (((CFile) item).getName().toLowerCase().contains(keyword.toLowerCase())) resultList.add((CFile) item); } } return resultList; } @Override public List<CFolder> searchFolder(@NonNull String keyword, CFolder folder) throws RequestFailException { List<Object> list = exploreFolder(folder, 0); List<CFolder> resultList = new ArrayList<>(); // search through every item's name for keyword match in case insensitive mode for (Object item : list) { if (CFolder.class.isAssignableFrom(item.getClass())) { if (((CFolder) item).getName().toLowerCase().contains(keyword.toLowerCase())) resultList.add((CFolder) item); } } return resultList; } @Override public List<Object> search(@NonNull String keyword, CFolder folder) throws RequestFailException { List<Object> list = exploreFolder(folder, 0); List<Object> resultList = new ArrayList<>(); // search through every item's name for keyword match in case insensitive mode for (Object item : list) { if (CFolder.class.isAssignableFrom(item.getClass())) { if (((CFolder) item).getName().toLowerCase().contains(keyword.toLowerCase())) resultList.add(item); } else if (CFile.class.isAssignableFrom(item.getClass())) { if (((CFile) item).getName().toLowerCase().contains(keyword.toLowerCase())) resultList.add(item); } } return resultList; } @Override public File getThumbnail(@NonNull CFile file) throws RequestFailException { if (TextUtils.isEmpty(mAccessToken)) { throw new RequestFailException("Access token not available"); } Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes/" + file.getId()).appendQueryParameter("asset", "ALL") .appendQueryParameter("tempLink", "true").build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); if (jsonObject.has("assets")) { JSONArray assets = jsonObject.getJSONArray("assets"); JSONObject asset = assets.getJSONObject(0); if (asset.has("tempLink")) { String fileUrl = asset.getString("tempLink"); String filename = asset.getString("name"); return downloadThumbnail(fileUrl, filename); } return null; } else { return null; } } else { throw new RequestFailException(response.message(), response.code()); } } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } catch (JSONException e) { e.printStackTrace(); } return null; } /** * Download thumbnail via the temperary url * * @param url * @param filename * @return * @throws RequestFailException */ private File downloadThumbnail(@NonNull String url, String filename) throws RequestFailException { Request request = new Request.Builder().url(url).get().build(); try { File localFile = new File(mContext.getFilesDir(), filename); Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { FilesUtils.copyFile(response.body().byteStream(), new FileOutputStream(localFile)); } else { throw new RequestFailException(response.message(), response.code()); } return localFile; } catch (IOException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } /** * Create files or folders from the JSONArray search result * * @param jsonArray that contain files and folders information * @return list that contains CFile and CFolder * @throws RequestFailException */ private List<Object> createItemList(JSONArray jsonArray) throws RequestFailException { if (jsonArray == null || jsonArray.length() == 0) return null; List<Object> list = new ArrayList<>(); for (int i = 0; i < jsonArray.length(); i++) { try { JSONObject jsonObject = jsonArray.getJSONObject(i); String type = jsonObject.getString("kind"); switch (type.toLowerCase()) { case "file": list.add(buildFile(jsonObject)); break; case "folder": list.add(buildFolder(jsonObject)); break; default: Log.e(TAG, "Unknown type found"); break; } } catch (JSONException e) { e.printStackTrace(); throw new RequestFailException(e.getMessage()); } } return list; } /** * Get root folder id * * @return root id as String */ private String getRootId() { Uri uri = Uri.parse(mMetadataUrl); String url = uri.buildUpon().appendEncodedPath("nodes") .appendQueryParameter("filters", "kind:FOLDER AND isRoot:true").build().toString(); Request request = new Request.Builder().url(url) .header("Authorization", String.format("Bearer %s", mAccessToken)).get().build(); try { Response response = mHttpClient.newCall(request).execute(); if (response.isSuccessful()) { JSONObject jsonObject = new JSONObject(response.body().string()); JSONArray entries = jsonObject.getJSONArray("data"); if (entries.length() > 0) { JSONObject root = entries.getJSONObject(0); return ROOT_ID = root.getString("id"); } } else { Log.e(TAG, response.message()); } } catch (JSONException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }