Java tutorial
/* * Copyright (C) 2003-2015 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.shareextension.service; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.exoplatform.R; import org.exoplatform.model.SocialPostInfo; import org.exoplatform.shareextension.service.Action.ActionListener; import org.exoplatform.shareextension.service.PostAction.PostActionListener; import org.exoplatform.singleton.DocumentHelper; import org.exoplatform.singleton.SocialServiceHelper; import org.exoplatform.social.client.api.SocialClientLibException; import org.exoplatform.social.client.api.model.RestActivity; import org.exoplatform.social.client.api.model.RestComment; import org.exoplatform.social.client.api.service.ActivityService; import org.exoplatform.utils.ExoConnectionUtils; import org.exoplatform.utils.ExoConstants; import org.exoplatform.utils.ExoDocumentUtils; import org.exoplatform.utils.ExoDocumentUtils.DocumentInfo; import org.exoplatform.utils.Log; import org.exoplatform.utils.TitleExtractor; import android.app.IntentService; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.v4.app.NotificationCompat; /** * Created by The eXo Platform SAS.<br/> * * @author Philippe Aristote paristote@exoplatform.com * @since Jun 4, 2015 */ public class ShareService extends IntentService { public static final String LOG_TAG = "____eXo____ShareService____"; public static final String POST_INFO = "postInfo"; private int notifId = 1; private SocialPostInfo postInfo; // key is uri in device, value is url on server private List<UploadInfo> uploadedMap = new ArrayList<UploadInfo>(); private enum ShareResult { SUCCESS, ERROR_INCORRECT_CONTENT_URI, ERROR_INCORRECT_ACCOUNT, ERROR_CREATE_FOLDER, ERROR_UPLOAD_FAILED, ERROR_POST_FAILED, ERROR_COMMENT_FAILED } public ShareService() { super("eXo_Share_Service"); } /* * We start here when the service is called */ @Override protected void onHandleIntent(Intent intent) { // Retrieve the content of the post from the intent postInfo = (SocialPostInfo) intent.getParcelableExtra(POST_INFO); // Notify the user that the share has started notifyBegin(); if (postInfo.ownerAccount == null) { notifyResult(ShareResult.ERROR_INCORRECT_ACCOUNT); return; } if (postInfo.hasAttachment()) { UploadInfo initUploadInfo = initUpload(); boolean uploadStarted = startUpload(initUploadInfo); if (uploadStarted) { boolean uploadedAll = doUpload(initUploadInfo); if (uploadedAll) { // already set templateParam when first doc upload completed doPost(); } } ; } else { // We don't have an attachment, maybe a link // TODO move as a separate Action - MOB-1866 String link = null; link = extractLinkFromText(); if (link != null) { postInfo.activityType = SocialPostInfo.TYPE_LINK; postInfo.postMessage = postInfo.postMessage.replace(link, String.format(Locale.US, "<a href=\"%s\">%s</a>", link, link)); } postInfo.templateParams = linkParams(link); doPost(); } } /** * Create the resources needed to create the upload destination folder and * upload the file */ private UploadInfo initUpload() { postInfo.activityType = SocialPostInfo.TYPE_DOC; UploadInfo uploadInfo = new UploadInfo(); uploadInfo.uploadId = Long.toHexString(System.currentTimeMillis()); uploadInfo.repository = DocumentHelper.getInstance().repository; uploadInfo.workspace = DocumentHelper.getInstance().workspace; if (postInfo.isPublic()) { // File will be uploaded in the Public folder of the user's drive // e.g. /Users/u___/us___/use___/user/Public/Mobile uploadInfo.drive = ExoConstants.DOCUMENT_PERSONAL_DRIVE_NAME; uploadInfo.folder = "Public/Mobile"; uploadInfo.jcrUrl = DocumentHelper.getInstance().getRepositoryHomeUrl(); } else { // File will be uploaded in the Documents folder of the space's drive // e.g. /Groups/spaces/the_space/Documents/Mobile uploadInfo.drive = ".spaces." + postInfo.destinationSpace.getOriginalName(); uploadInfo.folder = "Mobile"; StringBuffer url = new StringBuffer(postInfo.ownerAccount.serverUrl) .append(ExoConstants.DOCUMENT_JCR_PATH).append("/").append(uploadInfo.repository).append("/") .append(uploadInfo.workspace).append("/Groups/spaces/") .append(postInfo.destinationSpace.getOriginalName()).append("/Documents"); uploadInfo.jcrUrl = url.toString(); } return uploadInfo; } /** * Create the directory where the files are stored on the server, if it does * not already exist. */ private boolean startUpload(UploadInfo uploadInfo) { return CreateFolderAction.execute(postInfo, uploadInfo, new ActionListener() { @Override public boolean onSuccess(String message) { return true; } @Override public boolean onError(String error) { notifyResult(ShareResult.ERROR_CREATE_FOLDER); return false; } }); } /** * Upload the file */ private boolean doUpload(UploadInfo initUploadInfo) { boolean uploadedAll = false; uploadedMap.clear(); UploadInfo uploadInfo = initUploadInfo; final int numberOfFiles = postInfo.postAttachedFiles.size(); for (int i = 0; i < numberOfFiles; i++) { // notify the start of the upload i / total notifyProgress(i + 1, numberOfFiles); // close the current open input stream if (uploadInfo != null && uploadInfo.fileToUpload != null) uploadInfo.fileToUpload.closeDocStream(); // Retrieve details of the document to upload if (i != 0) { uploadInfo = new UploadInfo(uploadInfo); } String fileUri = "file://" + postInfo.postAttachedFiles.get(i); Uri uri = Uri.parse(fileUri); uploadInfo.fileToUpload = ExoDocumentUtils.documentInfoFromUri(uri, getBaseContext()); if (uploadInfo.fileToUpload == null) { notifyResult(ShareResult.ERROR_INCORRECT_CONTENT_URI); return false; } else { uploadInfo.fileToUpload.documentName = ExoDocumentUtils .cleanupFilename(uploadInfo.fileToUpload.documentName); } uploadedAll = UploadAction.execute(postInfo, uploadInfo, new ActionListener() { @Override public boolean onSuccess(String message) { return true; } @Override public boolean onError(String error) { notifyResult(ShareResult.ERROR_UPLOAD_FAILED); return false; } }); if (uploadInfo != null && uploadInfo.fileToUpload != null) uploadInfo.fileToUpload.closeDocStream(); if (!uploadedAll) { if (Log.LOGD) Log.e(LOG_TAG, String.format("Failed to upload file %d/%d : %s (doUpload)", i + 1, numberOfFiles, fileUri)); break; } if (uploadedAll) { uploadInfo.uploadedUrl = getDocUrl(uploadInfo); if (Log.LOGD) Log.d(LOG_TAG, String.format("Uploaded file %d/%d OK %s (doUpload)", i + 1, numberOfFiles, fileUri)); if (i == 0) postInfo.templateParams = docParams(uploadInfo); else { uploadedMap.add(uploadInfo); } } // Delete file after upload File f = new File(postInfo.postAttachedFiles.get(i)); if (Log.LOGD) Log.d(LOG_TAG, "File " + f.getName() + " deleted: " + (f.delete() ? "YES" : "NO")); } return uploadedAll; } /** * Post the message */ private boolean doPost() { RestActivity createdAct = PostAction.execute(postInfo, new PostActionListener()); boolean ret = createdAct != null; if (ret) { if (Log.LOGD) Log.d(LOG_TAG, "Post activity done"); for (UploadInfo commentInfo : uploadedMap) { ret = doComment(createdAct, commentInfo); if (!ret) break; if (Log.LOGD) Log.d(LOG_TAG, "Comment activity done"); } // Share finished successfully // Needed to avoid some problems when reopening the app if (ret) { ExoConnectionUtils.loggingOut(); // Notify notifyResult(ShareResult.SUCCESS); } else notifyResult(ShareResult.ERROR_COMMENT_FAILED); } else notifyResult(ShareResult.ERROR_POST_FAILED); return ret; } /** * Post a comment on an activity * * @param restAct the activity to comment * @param commentInfo the info to put in the comment * @return */ private boolean doComment(RestActivity restAct, UploadInfo commentInfo) { // TODO create a Comment Action to delegate the operation boolean ret = false; String mimeType = (commentInfo == null ? null : (commentInfo.fileToUpload == null ? null : commentInfo.fileToUpload.documentMimeType)); StringBuilder bld = new StringBuilder(); // append link bld.append("<a href=\"").append(commentInfo.uploadedUrl).append("\">") .append(commentInfo.fileToUpload.documentName).append("</a>"); // add image in the comment's body if (mimeType != null && mimeType.startsWith("image/")) { String src = commentInfo.uploadedUrl.replace("/jcr/", "/thumbnailImage/large/"); bld.append("<br/><a href=\"").append(commentInfo.uploadedUrl).append("\"><img src=\"").append(src) .append("\" /></a>"); } ActivityService<RestActivity> activityService = SocialServiceHelper.getInstance().activityService; RestComment restComment = new RestComment(); restComment.setText(bld.toString()); try { ret = activityService.createComment(restAct, restComment) != null; } catch (SocialClientLibException e) { if (Log.LOGD) Log.e(LOG_TAG, Log.getStackTraceString(e)); } return ret; } private String getDocUrl(UploadInfo pUploadInfo) { return pUploadInfo.jcrUrl + "/" + pUploadInfo.folder + "/" + pUploadInfo.fileToUpload.documentName; } private Map<String, String> docParams(UploadInfo pUploadInfo) { // Create and return TemplateParams for a DOC_ACTIVITY String docUrl = pUploadInfo.uploadedUrl; Map<String, String> templateParams = new HashMap<String, String>(); templateParams.put("WORKSPACE", pUploadInfo.workspace); templateParams.put("REPOSITORY", pUploadInfo.repository); String docLink = docUrl.substring(postInfo.ownerAccount.serverUrl.length()); templateParams.put("DOCLINK", docLink); StringBuffer beginPath = new StringBuffer(ExoConstants.DOCUMENT_JCR_PATH).append("/") .append(pUploadInfo.repository).append("/").append(pUploadInfo.workspace); String docPath = docLink.substring(beginPath.length()); templateParams.put("DOCPATH", docPath); templateParams.put("DOCNAME", pUploadInfo.fileToUpload.documentName); if (!postInfo.isPublic()) { templateParams.put("mimeType", pUploadInfo.fileToUpload.documentMimeType); } return templateParams; } private Map<String, String> linkParams(String link) { // Create and return TemplateParams for a LINK_ACTIVITY // Return null if there is no link if (link == null) return null; Map<String, String> templateParams = new HashMap<String, String>(); templateParams.put("comment", postInfo.postMessage); templateParams.put("link", link); templateParams.put("description", ""); templateParams.put("image", ""); try { templateParams.put("title", TitleExtractor.getPageTitle(link)); } catch (IOException e) { Log.e(LOG_TAG, "Cannot retrieve link title", e); templateParams.put("title", link); } return templateParams; } private String extractLinkFromText() { String text = postInfo.postMessage; // Find an occurrence of http:// or https:// // And return the corresponding URL if any int posHttp = text.indexOf("http://"); int posHttps = text.indexOf("https://"); int startOfLink = -1; if (posHttps > -1) startOfLink = posHttps; else if (posHttp > -1) startOfLink = posHttp; if (startOfLink > -1) { int endOfLink = text.indexOf(' ', startOfLink); if (endOfLink == -1) return text.substring(startOfLink); else return text.substring(startOfLink, endOfLink); } else { return null; } } /** * Send a local notification to inform that the share has started */ private void notifyBegin() { notifId = (int) System.currentTimeMillis(); String title = postInfo.hasAttachment() ? getString(R.string.ShareDocumentTitle) : getString(R.string.ShareMessageTitle); String text = postInfo.hasAttachment() ? getString(R.string.ShareDocumentText) : getString(R.string.ShareMessageText); NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()); builder.setSmallIcon(R.drawable.application_icon); builder.setContentTitle(title); builder.setContentText(text); builder.setAutoCancel(true); builder.setProgress(0, 0, true); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(notifId, builder.build()); } /** * Send a local notification to inform of the progress. Only called if the * share contains 1 or more attachments. * * @param current the index of the current file being uploaded * @param total the total number of files to upload */ private void notifyProgress(int current, int total) { String text = String.format(Locale.US, "%s (%d/%d)", getString(R.string.ShareDocumentText), current, total); NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()); builder.setSmallIcon(R.drawable.application_icon); builder.setContentTitle(getString(R.string.ShareDocumentTitle)); builder.setContentText(text); builder.setAutoCancel(true); builder.setProgress(0, 0, true); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(notifId, builder.build()); } /** * Notify the end of the sharing. The message depends on the given result. * * @param result one of {@link ShareResult} values */ private void notifyResult(ShareResult result) { String text = ""; switch (result) { case ERROR_CREATE_FOLDER: text = getString(R.string.ShareErrorUploadFolderFailed); break; case ERROR_INCORRECT_ACCOUNT: text = getString(R.string.ShareErrorIncorrectAccount); break; case ERROR_INCORRECT_CONTENT_URI: text = getString(R.string.ShareErrorCannotReadDoc); break; case ERROR_POST_FAILED: text = getString(R.string.ShareErrorPostFailed); break; case ERROR_COMMENT_FAILED: text = getString(R.string.ShareErrorCommentFailed); break; case ERROR_UPLOAD_FAILED: text = getString(R.string.ShareErrorUploadFailed); break; case SUCCESS: text = getString(R.string.ShareOperationSuccess); break; default: break; } String title = postInfo.hasAttachment() ? getString(R.string.ShareDocumentTitle) : getString(R.string.ShareMessageTitle); NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()); builder.setSmallIcon(R.drawable.application_icon); builder.setContentTitle(title); builder.setContentText(text); builder.setAutoCancel(true); builder.setProgress(0, 0, false); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(notifId, builder.build()); } public static class UploadInfo { public String uploadId; public DocumentInfo fileToUpload; public String repository; public String workspace; public String drive; public String folder; public String jcrUrl; public String uploadedUrl; public UploadInfo() { super(); } public UploadInfo(UploadInfo another) { uploadId = Long.toHexString(System.currentTimeMillis()); this.repository = another.repository; this.workspace = another.workspace; this.drive = another.drive; this.folder = another.folder; this.jcrUrl = another.jcrUrl; } } }