View Javadoc
1   /*
2    * Copyright (C) 2003-2015 eXo Platform SAS.
3    *
4    * This is free software; you can redistribute it and/or modify it
5    * under the terms of the GNU Lesser General Public License as
6    * published by the Free Software Foundation; either version 3 of
7    * the License, or (at your option) any later version.
8    *
9    * This software is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this software; if not, write to the Free
16   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
18   */
19  package org.exoplatform.shareextension;
20  
21  import java.io.BufferedInputStream;
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.lang.ref.WeakReference;
26  import java.net.MalformedURLException;
27  import java.net.URI;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Set;
33  
34  import org.apache.http.HttpResponse;
35  
36  import org.exoplatform.R;
37  import org.exoplatform.model.ExoAccount;
38  import org.exoplatform.model.SocialPostInfo;
39  import org.exoplatform.model.SocialSpaceInfo;
40  import org.exoplatform.shareextension.service.ShareService;
41  import org.exoplatform.singleton.AccountSetting;
42  import org.exoplatform.singleton.ServerSettingHelper;
43  import org.exoplatform.singleton.SocialServiceHelper;
44  import org.exoplatform.social.client.api.ClientServiceFactory;
45  import org.exoplatform.social.client.api.SocialClientContext;
46  import org.exoplatform.social.client.api.SocialClientLibException;
47  import org.exoplatform.social.client.api.service.VersionService;
48  import org.exoplatform.social.client.core.ClientServiceFactoryHelper;
49  import org.exoplatform.ui.social.SpaceSelectorActivity;
50  import org.exoplatform.utils.ExoConnectionUtils;
51  import org.exoplatform.utils.ExoConstants;
52  import org.exoplatform.utils.ExoDocumentUtils;
53  import org.exoplatform.utils.ExoDocumentUtils.DocumentInfo;
54  import org.exoplatform.utils.Log;
55  
56  import android.annotation.SuppressLint;
57  import android.app.AlertDialog;
58  import android.content.Context;
59  import android.content.DialogInterface;
60  import android.content.DialogInterface.OnClickListener;
61  import android.content.Intent;
62  import android.content.pm.PackageManager;
63  import android.graphics.Bitmap;
64  import android.graphics.Bitmap.CompressFormat;
65  import android.graphics.BitmapFactory;
66  import android.net.Uri;
67  import android.os.AsyncTask;
68  import android.os.AsyncTask.Status;
69  import android.os.Bundle;
70  import android.provider.Settings;
71  import android.support.v4.app.Fragment;
72  import android.support.v4.app.FragmentActivity;
73  import android.support.v4.app.FragmentTransaction;
74  import android.text.format.DateFormat;
75  import android.view.Menu;
76  import android.view.MenuItem;
77  import android.view.View;
78  import android.view.inputmethod.InputMethodManager;
79  import android.widget.Button;
80  import android.widget.Toast;
81  import android.widget.ViewFlipper;
82  
83  /**
84   * Created by The eXo Platform SAS
85   * 
86   * @author Philippe Aristote paristote@exoplatform.com
87   * @since 3 Jun 2015
88   */
89  public class ShareActivity extends FragmentActivity {
90  
91    /**
92     * Direction of the animation to switch from one fragment to another
93     */
94    public static enum Anim {
95      NO_ANIM, FROM_LEFT, FROM_RIGHT
96    }
97  
98    public static final String       LOG_TAG                  = "____eXo____Share_Extension____";
99  
100   public static final String       DEFAULT_CONTENT_NAME     = "TEMP_FILE_TO_SHARE";
101 
102   // type of button in actionBar, for invisible button
103   public static final int          BUTTON_TYPE_INVISIBLE    = 0;
104 
105   // use id of share button in share_button_wrapper
106   public static final int          BUTTON_TYPE_SHARE        = R.id.share_button;
107 
108   // use id of signin button in share_button_wrapper
109   public static final int          BUTTON_TYPE_SIGNIN       = R.id.signin_login_btn;
110 
111   private static final int         SELECT_SHARE_DESTINATION = 11;
112 
113   // position index of view in share_button_wrapper file
114   private static final int         SHARE_BUTTON_INDEX       = 0;
115 
116   private static final int         PROGRESS_INDEX           = 1;
117 
118   private static final int         SIGNIN_BUTTON_INDEX      = 2;
119 
120   private boolean                  online;
121 
122   // post button
123   private Button                   mainButton;
124 
125   // signin button
126   private Button                   mSignInButton;
127 
128   private ViewFlipper              mButtonWrapper;
129 
130   private WeakReference<LoginTask> mLoginRef;
131 
132   private MenuItem                 mMenuItem;
133 
134   private boolean                  mBtnEnable               = false;
135 
136   private SocialPostInfo           postInfo;
137 
138   private boolean                  mMultiFlag               = false;
139 
140   private List<Uri>                mAttachmentUris;
141 
142   @Override
143   protected void onCreate(Bundle bundle) {
144     super.onCreate(bundle);
145 
146     setContentView(R.layout.share_extension_activity);
147     setTitle(R.string.ShareTitle);
148     online = false;
149     postInfo = new SocialPostInfo();
150 
151     if (isIntentCorrect()) {
152       Intent intent = getIntent();
153       String type = intent.getType();
154       if ("text/plain".equals(type)) {
155         // The share does not contain an attachment
156         // TODO extract the link info - MOB-1866
157         postInfo.postMessage = intent.getStringExtra(Intent.EXTRA_TEXT);
158       } else {
159         // The share contains an attachment
160         if (mMultiFlag) {
161           mAttachmentUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
162         } else {
163           Uri contentUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
164           if (contentUri != null) {
165             mAttachmentUris = new ArrayList<Uri>();
166             mAttachmentUris.add(contentUri);
167           }
168           if (Log.LOGD) {
169             Log.d(LOG_TAG, "Number of files to share: ", mAttachmentUris.size());
170           }
171         }
172         prepareAttachmentsAsync();
173       }
174 
175       init();
176 
177       // Create and display the composer, aka ComposeFragment
178       ComposeFragment composer = ComposeFragment.getFragment();
179       openFragment(composer, ComposeFragment.COMPOSE_FRAGMENT, Anim.NO_ANIM);
180     } else {
181       // We're not supposed to reach this activity by anything else than an
182       // ACTION_SEND intent
183       finish();
184     }
185 
186   }
187 
188   private boolean isIntentCorrect() {
189     Intent intent = getIntent();
190     String action = intent.getAction();
191     String type = intent.getType();
192     mMultiFlag = Intent.ACTION_SEND_MULTIPLE.equals(action);
193     return ((Intent.ACTION_SEND.equals(action) || mMultiFlag) && type != null);
194   }
195 
196   private void init() {
197     // Load the list of accounts
198     ServerSettingHelper.getInstance().getServerInfoList(this);
199     // Init the activity with the selected account
200     postInfo.ownerAccount = AccountSetting.getInstance().getCurrentAccount();
201     if (postInfo.ownerAccount == null) {
202       List<ExoAccount> serverList = ServerSettingHelper.getInstance().getServerInfoList(this);
203       if (serverList == null || serverList.size() == 0) {
204         // TODO open the configuration assistant to create an account
205         // Then come back here after the result
206         Toast.makeText(this, R.string.ShareErrorNoAccountConfigured, Toast.LENGTH_LONG).show();
207         finish();
208       }
209       int selectedServerIdx = Integer.parseInt(getSharedPreferences(ExoConstants.EXO_PREFERENCE, 0).getString(
210                                                                                                               ExoConstants.EXO_PRF_DOMAIN_INDEX,
211                                                                                                               "-1"));
212       AccountSetting.getInstance().setDomainIndex(String.valueOf(selectedServerIdx));
213       AccountSetting.getInstance().setCurrentAccount((selectedServerIdx == -1
214           || selectedServerIdx >= serverList.size()) ? null : serverList.get(selectedServerIdx));
215       postInfo.ownerAccount = AccountSetting.getInstance().getCurrentAccount();
216     }
217     ExoAccount acc = postInfo.ownerAccount;
218     if (acc != null && acc.password != null && !"".equals(acc.password)) {
219       // Try to login to setup the social services
220       loginWithSelectedAccount();
221     }
222   }
223 
224   @Override
225   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
226     // When we come back from the space selector activity
227     // TODO make it another fragment
228     if (requestCode == SELECT_SHARE_DESTINATION) {
229       if (resultCode == RESULT_OK) {
230         SocialSpaceInfo space = (SocialSpaceInfo) data.getParcelableExtra(SpaceSelectorActivity.SELECTED_SPACE);
231         ComposeFragment composer = ComposeFragment.getFragment();
232         if (space != null) {
233           composer.setSpaceSelectorLabel(space.displayName);
234         } else {
235           composer.setSpaceSelectorLabel(getResources().getString(R.string.Public));
236         }
237         postInfo.destinationSpace = space;
238       }
239     }
240   }
241 
242   /**
243    * Util method to switch from one fragment to another, with an animation
244    * 
245    * @param toOpen The Fragment to open in this transaction
246    * @param key the string key of the fragment
247    * @param anim the type of animation
248    */
249   public void openFragment(Fragment toOpen, String key, Anim anim) {
250     FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
251     switch (anim) {
252     case FROM_LEFT:
253       tr.setCustomAnimations(R.anim.fragment_enter_ltr, R.anim.fragment_exit_ltr);
254       break;
255     case FROM_RIGHT:
256       tr.setCustomAnimations(R.anim.fragment_enter_rtl, R.anim.fragment_exit_rtl);
257       break;
258     default:
259     case NO_ANIM:
260       break;
261     }
262     tr.replace(R.id.share_extension_fragment, toOpen, key);
263     tr.commit();
264   }
265 
266   @Override
267   public void onBackPressed() {
268     // Intercept the back button taps to display previous state with animation
269     // If we're on the composer, call super to finish the activity
270     ComposeFragment composer = ComposeFragment.getFragment();
271     if (composer.isAdded()) {
272       if (postInfo != null)
273         ExoDocumentUtils.deleteLocalFiles(postInfo.postAttachedFiles);
274       super.onBackPressed();
275     } else if (AccountsFragment.getFragment().isAdded()) {
276       // close the accounts fragment and reopen the composer fragment
277       openFragment(composer, ComposeFragment.COMPOSE_FRAGMENT, Anim.FROM_LEFT);
278     } else if (SignInFragment.getFragment().isAdded()) {
279       // close the sign in fragment and reopen the accounts fragment
280       openFragment(AccountsFragment.getFragment(), AccountsFragment.ACCOUNTS_FRAGMENT, Anim.FROM_LEFT);
281     }
282   }
283 
284   @Override
285   protected void onDestroy() {
286     if (ComposeFragment.getFragment() != null)
287       ComposeFragment.getFragment().setThumbnailImage(null);
288     super.onDestroy();
289   }
290 
291   /*
292    * GETTERS & SETTERS
293    */
294 
295   public void setPostMessage(String message) {
296     if (message != null) {
297       postInfo.postMessage = message;
298     }
299   }
300 
301   public SocialPostInfo getPostInfo() {
302     return postInfo;
303   }
304 
305   @Override
306   public boolean onCreateOptionsMenu(Menu menu) {
307     Log.d("TEST", "onCreateOptionsMenu");
308     getMenuInflater().inflate(R.menu.share, menu);
309     mMenuItem = menu.findItem(R.id.menu_shareextension_post);
310     if (mMenuItem != null) {
311       View v = mMenuItem.getActionView();
312       if (v != null) {
313         mButtonWrapper = (ViewFlipper) v.findViewById(R.id.share_button_wrapper);
314         mainButton = (Button) v.findViewById(R.id.share_button);
315         mSignInButton = (Button) v.findViewById(R.id.signin_login_btn);
316         toggleMainButtonType(BUTTON_TYPE_SHARE);
317         enableDisableMainButton(mBtnEnable);
318         LoginTask task = mLoginRef == null ? null : mLoginRef.get();
319         if (task != null && task.getStatus() == Status.RUNNING) {
320           toggleProgressVisible(true);
321         }
322       }
323     }
324     return true;
325   }
326 
327   /**
328    * @param type pass 0 for invisible the button
329    */
330   public void toggleMainButtonType(int type) {
331     if (mMenuItem == null)
332       return;
333     if (type == BUTTON_TYPE_SIGNIN) {
334       // switch from post => signin
335       mMenuItem.setVisible(true);
336       mMenuItem.setTitle(R.string.SignInInformation);
337     } else if (type == BUTTON_TYPE_SHARE) {
338       // switch from signin => post
339       mMenuItem.setVisible(true);
340       mMenuItem.setTitle(R.string.StatusUpdate);
341     } else { // all other case treat as invisible case
342       type = BUTTON_TYPE_INVISIBLE;
343       mMenuItem.setVisible(false);
344     }
345     mButtonWrapper.setTag(type);
346   }
347 
348   private int getButtonIndexFromTag() {
349     // default show share button
350     int ret = SHARE_BUTTON_INDEX;
351     if (mButtonWrapper != null && mButtonWrapper.getTag() instanceof Integer) {
352       final int type = (Integer) mButtonWrapper.getTag();
353       if (type == BUTTON_TYPE_SIGNIN) {
354         // case signin button
355         ret = SIGNIN_BUTTON_INDEX;
356       } else if (type == BUTTON_TYPE_SHARE) {
357         // case share button
358         ret = SHARE_BUTTON_INDEX;
359       }
360     }
361     return ret;
362   }
363 
364   public boolean isProgressVisible() {
365     return (mButtonWrapper != null && mButtonWrapper.getDisplayedChild() == PROGRESS_INDEX);
366   }
367 
368   public void toggleProgressVisible(boolean visible) {
369     if (mButtonWrapper != null) {
370       mButtonWrapper.setDisplayedChild(visible ? PROGRESS_INDEX : getButtonIndexFromTag());
371     }
372   }
373 
374   /**
375    * Switch the main button to the given enabled state. If the main button is
376    * the post button, we also check that the account is online.
377    * 
378    * @param enabled
379    */
380   public void enableDisableMainButton(boolean enabled) {
381     mBtnEnable = enabled;
382     if (mMenuItem == null) {
383       return;
384     }
385     if (mainButton != null) {
386       boolean currentState = mainButton.isEnabled();
387       if (currentState != enabled) {
388         mainButton.setEnabled(enabled && online);
389       }
390     }
391     if (mSignInButton != null) {
392       boolean currentState = mSignInButton.isEnabled();
393       if (currentState != enabled) {
394         mSignInButton.setEnabled(enabled);
395       }
396     }
397   }
398 
399   /*
400    * CLICK LISTENERS
401    */
402 
403   public void onMainButtonClicked(View view) {
404     // Tap on the Post button
405     final int id = view.getId();
406     if (id == BUTTON_TYPE_SHARE) {
407       if (postInfo.ownerAccount == null || !online) {
408         Toast.makeText(this, R.string.ShareCannotPostBecauseOffline, Toast.LENGTH_LONG).show();
409         return;
410       }
411 
412       String postMessage = postInfo.postMessage;
413       if (postMessage == null || "".equals(postMessage))
414         return;
415 
416       Log.d(LOG_TAG, "Start share service...");
417       Intent share = new Intent(getBaseContext(), ShareService.class);
418       share.putExtra(ShareService.POST_INFO, postInfo);
419       startService(share);
420       Toast.makeText(getBaseContext(), R.string.ShareOperationStarted, Toast.LENGTH_LONG).show();
421 
422       // Post is in progress, our work is done here
423       finish();
424     } else if (id == BUTTON_TYPE_SIGNIN) {
425       // Tap on the Sign In button
426       postInfo.ownerAccount.password = SignInFragment.getFragment().getPassword();
427       openFragment(ComposeFragment.getFragment(), ComposeFragment.COMPOSE_FRAGMENT, Anim.FROM_LEFT);
428       loginWithSelectedAccount();
429     }
430   }
431 
432   private void hideSoftKeyboard() {
433     InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
434     mgr.hideSoftInputFromWindow(ComposeFragment.getFragment().getEditText().getWindowToken(), 0);
435   }
436 
437   public void onSelectAccount() {
438     // Called when the select account field is tapped
439     hideSoftKeyboard();
440     openFragment(AccountsFragment.getFragment(), AccountsFragment.ACCOUNTS_FRAGMENT, Anim.FROM_RIGHT);
441   }
442 
443   public void onAccountSelected(ExoAccount account) {
444     // Called when an account with password was selected.
445     // If the selected account has no password, we open the SignInFragment first
446     if (!account.equals(postInfo.ownerAccount)) {
447       online = false;
448       postInfo.ownerAccount = account;
449       loginWithSelectedAccount();
450     }
451   }
452 
453   public void onSelectSpace() {
454     // Called when the select space field is tapped
455     if (online) {
456       Intent spaceSelector = new Intent(this, SpaceSelectorActivity.class);
457       startActivityForResult(spaceSelector, SELECT_SHARE_DESTINATION);
458     } else {
459       Toast.makeText(this, R.string.ShareCannotSelectSpaceBecauseOffline, Toast.LENGTH_LONG).show();
460     }
461   }
462 
463   /*
464    * TASKS
465    */
466 
467   public void loginWithSelectedAccount() {
468     LoginTask task = new LoginTask();
469     mLoginRef = new WeakReference<ShareActivity.LoginTask>(task);
470     task.execute(postInfo.ownerAccount);
471   }
472 
473   private void prepareAttachmentsAsync() {
474     if (!ExoDocumentUtils.didRequestPermission(this, ExoConstants.REQUEST_PICK_IMAGE_FROM_GALLERY)) {
475       new PrepareAttachmentsTask().execute();
476     }
477   }
478   
479   @SuppressLint("Override")
480   @Override
481   public void onRequestPermissionsResult(int reqCode, String[] permissions, int[] results) {
482     if (reqCode == ExoConstants.REQUEST_PICK_IMAGE_FROM_GALLERY) {
483       if (results.length > 0
484           && results[0] == PackageManager.PERMISSION_GRANTED) {  
485           // permission granted
486           prepareAttachmentsAsync();
487       } else {
488           // permission denied
489         AlertDialog.Builder db = new AlertDialog.Builder(this);
490         DialogInterface.OnClickListener dialogInterface = new OnClickListener() {
491           @Override
492           public void onClick(DialogInterface dialog, int which) {
493             if (which == DialogInterface.BUTTON_NEUTRAL) {
494               Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
495                                          Uri.fromParts("package", getPackageName(), null));
496               intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
497               startActivity(intent);
498             }
499             finish();
500           }
501         };
502         db.setMessage(R.string.ShareErrorStoragePermissionDenied)
503           .setNegativeButton(R.string.ShareErrorDialogLeave, dialogInterface)
504           .setNeutralButton(R.string.ShareErrorDialogAppSettings, dialogInterface);
505         AlertDialog dialog = db.create();
506         dialog.show();
507       }
508       return;
509     }
510   }
511 
512   /**
513    * Performs these operations asynchronously:
514    * <ol>
515    * <li>Check each file URI in mAttachmentsUris</li>
516    * <li>If the file is less than 10MB, add it to postInfo</li>
517    * <li>Stop after 10 files</li>
518    * <li>Generate a bitmap for the thumbnail</li>
519    * <li>Wait until the Compose fragment is ready and display the thumbnail</li>
520    * </ol>
521    * 
522    * @author paristote
523    */
524   private class PrepareAttachmentsTask extends AsyncTask<Void, Void, Void> {
525 
526     private Bitmap thumbnail = null;
527 
528     private String errorMessage;
529 
530     private Bitmap getThumbnail(File origin) {
531       BitmapFactory.Options opts = new BitmapFactory.Options();
532       opts.inSampleSize = 4;
533       opts.inPreferredConfig = Bitmap.Config.RGB_565;
534       Bitmap thumbnail = BitmapFactory.decodeFile(origin.getAbsolutePath(), opts);
535       return thumbnail;
536     }
537 
538     private File getFileWithRotatedBitmap(DocumentInfo info, String filename) throws IOException {
539       FileOutputStream fos = null;
540       try {
541         // Decode bitmap from input stream
542         Bitmap bm = BitmapFactory.decodeStream(info.documentData);
543         // Turn the image in the correct orientation
544         bm = ExoDocumentUtils.rotateBitmapByAngle(bm, info.orientationAngle);
545         File file = new File(getFilesDir(), filename);
546         fos = new FileOutputStream(file);
547         bm.compress(CompressFormat.JPEG, 100, fos);
548         fos.flush();
549         return file;
550       } catch (OutOfMemoryError e) {
551         throw new RuntimeException("Exception while decoding/rotating the bitmap", e);
552       } finally {
553         try {
554           // try..catch here to not break the process if close() fails
555           if (fos != null)
556             fos.close();
557         } catch (IOException e) {
558         }
559       }
560     }
561 
562     private File getFileWithData(DocumentInfo info, String filename) throws IOException {
563       FileOutputStream fileOutput = null;
564       BufferedInputStream buffInput = null;
565       try {
566         // create temp file
567         fileOutput = openFileOutput(filename, MODE_PRIVATE);
568         buffInput = new BufferedInputStream(info.documentData);
569         byte[] buf = new byte[1024];
570         int len;
571         while ((len = buffInput.read(buf)) != -1) {
572           fileOutput.write(buf, 0, len);
573         }
574         File file = new File(getFilesDir(), filename);
575         return file;
576       } finally {
577         try {
578           // try..catch here to not break the process if close() fails
579           if (buffInput != null)
580             buffInput.close();
581           if (fileOutput != null)
582             fileOutput.close();
583         } catch (IOException e) {
584         }
585       }
586     }
587 
588     @Override
589     protected Void doInBackground(Void... params) {
590       Set<Integer> errors = new HashSet<Integer>();
591       if (mAttachmentUris != null && !mAttachmentUris.isEmpty()) {
592         postInfo.postAttachedFiles = new ArrayList<String>(ExoConstants.SHARE_EXTENSION_MAX_ITEMS);
593         for (Uri att : mAttachmentUris) {
594           // Stop when we reach the maximum number of files
595           if (postInfo.postAttachedFiles.size() == ExoConstants.SHARE_EXTENSION_MAX_ITEMS) {
596             errors.add(R.string.ShareErrorTooManyFiles);
597             break;
598           }
599           DocumentInfo info = ExoDocumentUtils.documentInfoFromUri(att, getApplicationContext());
600           // Skip if the file cannot be read
601           if (info == null) {
602             errors.add(R.string.ShareErrorCannotReadDoc);
603             continue;
604           }
605           // Skip if the file is more than 10MB
606           if (info.documentSizeKb > (ExoConstants.SHARE_EXTENSION_MAX_SIZE_MB * 1024)) {
607             errors.add(R.string.ShareErrorFileTooBig);
608             continue;
609           }
610           // All good, let's copy this file in our app's storage
611           // We must do this because some sharing apps (e.g. Google Photos)
612           // will revoke the permission on the files when the activity stops,
613           // therefore the service won't be able to access them
614           String cleanName = ExoDocumentUtils.cleanupFilename(info.documentName);
615           String tempFileName = DateFormat.format("yyyy-MM-dd-HH:mm:ss", System.currentTimeMillis()) + "-" + cleanName;
616 
617           try {
618             // Create temp file
619             File tempFile = null;
620             if ("image/jpeg".equals(info.documentMimeType) && info.orientationAngle != ExoDocumentUtils.ROTATION_0) {
621               // For an image with an EXIF rotation information, we get the file
622               // from the bitmap rotated back to its correct orientation
623               tempFile = getFileWithRotatedBitmap(info, tempFileName);
624             } else {
625               // Otherwise we just write the data to a file
626               tempFile = getFileWithData(info, tempFileName);
627             }
628             // add file to list
629             postInfo.postAttachedFiles.add(tempFile.getAbsolutePath());
630             if (thumbnail == null) {
631               thumbnail = getThumbnail(tempFile);
632             }
633           } catch (Exception e) {
634             errors.add(R.string.ShareErrorCannotReadDoc);
635           }
636         }
637         // Done creating the files
638         // Create an error message (if any) to display in onPostExecute
639         if (!errors.isEmpty()) {
640           StringBuilder message;
641           if (postInfo.postAttachedFiles.size() == 0)
642             message = new StringBuilder(getString(R.string.ShareErrorAllFilesCannotShare)).append(":");
643           else
644             message = new StringBuilder(getString(R.string.ShareErrorSomeFilesCannotShare)).append(":");
645           for (Integer errCode : errors) {
646             switch (errCode) {
647             case R.string.ShareErrorCannotReadDoc:
648             case R.string.ShareErrorFileTooBig:
649             case R.string.ShareErrorTooManyFiles:
650               message.append("\n").append(getString(errCode));
651               break;
652             }
653           }
654           errorMessage = message.toString();
655         }
656         while (ComposeFragment.getFragment() == null) {
657           // Wait until the compose fragment is ready
658         }
659       }
660       return null;
661     }
662 
663     @Override
664     protected void onPostExecute(Void result) {
665       ComposeFragment.getFragment().setThumbnailImage(thumbnail);
666       if (postInfo.postAttachedFiles != null)
667         ComposeFragment.getFragment().setNumberOfAttachments(postInfo.postAttachedFiles.size());
668       if (errorMessage != null)
669         Toast.makeText(ShareActivity.this, errorMessage, Toast.LENGTH_LONG).show();
670     };
671   }
672 
673   /**
674    * Perform these operations in background:
675    * <ol>
676    * <li>Logout any currently logged in account</li>
677    * <li>Login with the given account</li>
678    * <li>If login is successful, setup the social client and services</li>
679    * </ol>
680    * 
681    * @author paristote
682    */
683   private class LoginTask extends AsyncTask<ExoAccount, Void, Integer> {
684 
685     @Override
686     protected void onPreExecute() {
687       toggleProgressVisible(true);
688       super.onPreExecute();
689     }
690 
691     @SuppressWarnings("unchecked")
692     @Override
693     protected Integer doInBackground(ExoAccount... accounts) {
694       ExoConnectionUtils.loggingOut();
695       String username = accounts[0].username;
696       String password = accounts[0].password;
697       String url = accounts[0].serverUrl + "/rest/private/platform/info";
698       try {
699         Log.d(LOG_TAG, String.format("Started login request to %s ...", url));
700         HttpResponse resp = ExoConnectionUtils.getPlatformResponse(username, password, url);
701         ExoConnectionUtils.checkPLFVersion(resp, accounts[0].serverUrl, username);
702         int result = ExoConnectionUtils.checkPlatformRespose(resp);
703         if (ExoConnectionUtils.LOGIN_SUCCESS == result) {
704           URL u = new URL(accounts[0].serverUrl);
705           SocialClientContext.setProtocol(u.getProtocol());
706           SocialClientContext.setHost(u.getHost());
707           SocialClientContext.setPort(u.getPort());
708           SocialClientContext.setPortalContainerName(ExoConstants.ACTIVITY_PORTAL_CONTAINER);
709           SocialClientContext.setRestContextName(ExoConstants.ACTIVITY_REST_CONTEXT);
710           SocialClientContext.setUsername(username);
711           SocialClientContext.setPassword(password);
712           ClientServiceFactory clientServiceFactory = ClientServiceFactoryHelper.getClientServiceFactory();
713           VersionService versionService = clientServiceFactory.createVersionService();
714           SocialClientContext.setRestVersion(versionService.getLatest());
715           SocialServiceHelper.getInstance().activityService = clientServiceFactory.createActivityService();
716           SocialServiceHelper.getInstance().spaceService = clientServiceFactory.createSpaceService();
717           SocialServiceHelper.getInstance().identityService = clientServiceFactory.createIdentityService();
718         }
719         return Integer.valueOf(result);
720       } catch (MalformedURLException e) {
721         Log.e(LOG_TAG, "Login task failed", e);
722       } catch (IOException e) {
723         Log.e(LOG_TAG, "Login task failed", e);
724       } catch (SocialClientLibException e) {
725         Log.e(LOG_TAG, "Login task failed", e);
726       } catch (Exception e) {
727         // XXX cannot replace because SocialClientLib can throw exceptions like
728         // ServerException, UnsupportMethod ,..
729         Log.e(LOG_TAG, "Login task failed", e);
730       }
731       return Integer.valueOf(ExoConnectionUtils.LOGIN_FAILED);
732     }
733 
734     @Override
735     protected void onCancelled() {
736       super.onCancelled();
737       toggleProgressVisible(false);
738     }
739 
740     @Override
741     protected void onPostExecute(Integer result) {
742       Log.d(LOG_TAG, String.format("Received login response %s", result));
743       if (ExoConnectionUtils.LOGIN_SUCCESS == result.intValue()) {
744         online = true;
745       } else {
746         Toast.makeText(getApplicationContext(), R.string.ShareErrorSignInFailed, Toast.LENGTH_LONG).show();
747         postInfo.ownerAccount.password = "";
748         online = false;
749       }
750       toggleProgressVisible(false);
751       enableDisableMainButton(online && !"".equals(ComposeFragment.getFragment().getPostMessage()));
752     }
753   }
754 
755   // TODO support for text content that contains an URL to download a file
756   // e.g. share from dropbox
757   @SuppressWarnings("unused")
758   private class DownloadTask extends AsyncTask<String, Void, String> {
759 
760     private String extractUriFromText(String text) {
761       int posHttp = text.indexOf("http://");
762       int posHttps = text.indexOf("https://");
763       int startOfLink = -1;
764       if (posHttps > -1)
765         startOfLink = posHttps;
766       else if (posHttp > -1)
767         startOfLink = posHttp;
768       if (startOfLink > -1) {
769         int endOfLink = text.indexOf(' ', startOfLink);
770         if (endOfLink == -1)
771           endOfLink = text.length() - startOfLink;
772         return text.substring(startOfLink, endOfLink);
773       } else {
774         return null;
775       }
776     }
777 
778     private String getUriWithoutQueryString(URI uri) {
779       StringBuffer buf = new StringBuffer(uri.getScheme()).append("://")
780                                                           .append(uri.getHost())
781                                                           .append(uri.getPort() != -1 ? ":" + uri.getPort() : "")
782                                                           .append(uri.getRawPath() != null ? uri.getRawPath() : "");
783       return buf.toString();
784     }
785 
786     private String getFileName(String decodedPath) {
787       if (decodedPath == null)
788         return DEFAULT_CONTENT_NAME;
789       if (decodedPath.endsWith("/"))
790         decodedPath = decodedPath.substring(0, decodedPath.length() - 1);
791       int beginNamePos = decodedPath.lastIndexOf('/');
792       if (beginNamePos < 0)
793         return DEFAULT_CONTENT_NAME;
794       String name = decodedPath.substring(beginNamePos + 1);
795       if (name == null || "".equals(name))
796         return DEFAULT_CONTENT_NAME;
797       else
798         return name;
799     }
800 
801     @Override
802     protected String doInBackground(String... params) {
803       String str = params[0];
804       if (str != null) {
805         try {
806           URI uri = URI.create(extractUriFromText(str));
807           String strUri = getUriWithoutQueryString(uri);
808           Log.d(LOG_TAG, "Started download of " + strUri);
809           BufferedInputStream in = new BufferedInputStream(new URL(strUri).openStream());
810           String fileName = getFileName(uri.getPath());
811           FileOutputStream out = openFileOutput(fileName, 0);
812           byte[] buf = new byte[1024];
813           int len;
814           while ((len = in.read(buf)) != -1) {
815             out.write(buf, 0, len);
816           }
817           out.close();
818           in.close();
819           // comment because DownloadTask is unused now, no need to update
820           // code to match multi share case
821           // postInfo.postAttachmentUri = new URI("file://" +
822           // getFileStreamPath(fileName).getAbsolutePath()).toString();
823           // isLocalFile = true;
824           // Log.d(LOG_TAG, "Download successful: ");
825         } catch (Exception e) {
826           Log.e(LOG_TAG, "Error ", e);
827         }
828       }
829       return null;
830     }
831   }
832 }