1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   3  *   Copyright (C) 2012-2013 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   9  *   This program 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 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.ui
.activity
; 
  21 import java
.util
.ArrayList
; 
  22 import java
.util
.List
; 
  24 import org
.apache
.http
.protocol
.HTTP
; 
  26 import android
.accounts
.Account
; 
  27 import android
.accounts
.AccountManager
; 
  28 import android
.accounts
.AccountManagerCallback
; 
  29 import android
.accounts
.AccountManagerFuture
; 
  30 import android
.accounts
.OperationCanceledException
; 
  31 import android
.content
.Intent
; 
  32 import android
.content
.pm
.PackageManager
; 
  33 import android
.content
.pm
.ResolveInfo
; 
  34 import android
.net
.Uri
; 
  35 import android
.os
.Bundle
; 
  36 import android
.os
.Parcelable
; 
  37 import android
.webkit
.MimeTypeMap
; 
  39 import com
.actionbarsherlock
.app
.SherlockFragmentActivity
; 
  40 import com
.owncloud
.android
.MainApp
; 
  41 import com
.owncloud
.android
.R
; 
  42 import com
.owncloud
.android
.authentication
.AccountUtils
; 
  43 import com
.owncloud
.android
.datamodel
.OCFile
; 
  45 import com
.owncloud
.android
.lib
.accounts
.OwnCloudAccount
; 
  46 import com
.owncloud
.android
.lib
.network
.webdav
.WebdavUtils
; 
  48 import com
.owncloud
.android
.utils
.Log_OC
; 
  52  * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s . 
  54  * @author David A. Velasco 
  56 public abstract class FileActivity 
extends SherlockFragmentActivity 
{ 
  58     public static final String EXTRA_FILE 
= "com.owncloud.android.ui.activity.FILE"; 
  59     public static final String EXTRA_ACCOUNT 
= "com.owncloud.android.ui.activity.ACCOUNT"; 
  60     public static final String EXTRA_WAITING_TO_PREVIEW 
= "com.owncloud.android.ui.activity.WAITING_TO_PREVIEW"; 
  61     public static final String EXTRA_FROM_NOTIFICATION
= "com.owncloud.android.ui.activity.FROM_NOTIFICATION"; 
  63     public static final String TAG 
= FileActivity
.class.getSimpleName();  
  66     /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */ 
  67     private Account mAccount
; 
  69     /** Main {@link OCFile} handled by the activity.*/ 
  72     /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account} */ 
  73     private boolean mRedirectingToSetupAccount 
= false
; 
  75     /** Flag to signal when the value of mAccount was set */  
  76     private boolean mAccountWasSet
; 
  78     /** Flag to signal when the value of mAccount was restored from a saved state */  
  79     private boolean mAccountWasRestored
; 
  81     /** Flag to signal if the activity is launched by a notification */ 
  82     private boolean mFromNotification
; 
  87      * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of  
  88      * the {@link FileActivity}. 
  90      * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user  
  91      * is requested to create a new one. 
  94     protected void onCreate(Bundle savedInstanceState
) { 
  95         super.onCreate(savedInstanceState
); 
  97         if(savedInstanceState 
!= null
) { 
  98             account 
= savedInstanceState
.getParcelable(FileActivity
.EXTRA_ACCOUNT
); 
  99             mFile 
= savedInstanceState
.getParcelable(FileActivity
.EXTRA_FILE
); 
 100             mFromNotification 
= savedInstanceState
.getBoolean(FileActivity
.EXTRA_FROM_NOTIFICATION
); 
 102             account 
= getIntent().getParcelableExtra(FileActivity
.EXTRA_ACCOUNT
); 
 103             mFile 
= getIntent().getParcelableExtra(FileActivity
.EXTRA_FILE
); 
 104             mFromNotification 
= getIntent().getBooleanExtra(FileActivity
.EXTRA_FROM_NOTIFICATION
, false
); 
 107         setAccount(account
, savedInstanceState 
!= null
); 
 113      *  Since ownCloud {@link Account}s can be managed from the system setting menu,  
 114      *  the existence of the {@link Account} associated to the instance must be checked  
 115      *  every time it is restarted. 
 118     protected void onRestart() { 
 120         boolean validAccount 
= (mAccount 
!= null 
&& AccountUtils
.setCurrentOwnCloudAccount(getApplicationContext(), mAccount
.name
)); 
 122             swapToDefaultAccount(); 
 129     protected void onStart() { 
 131         if (mAccountWasSet
) { 
 132             onAccountSet(mAccountWasRestored
); 
 138      *  Sets and validates the ownCloud {@link Account} associated to the Activity.  
 140      *  If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. 
 142      *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.  
 144      *  @param account          New {@link Account} to set. 
 145      *  @param savedAccount     When 'true', account was retrieved from a saved instance state. 
 147     private void setAccount(Account account
, boolean savedAccount
) { 
 148         Account oldAccount 
= mAccount
; 
 149         boolean validAccount 
= (account 
!= null 
&& AccountUtils
.setCurrentOwnCloudAccount(getApplicationContext(), account
.name
)); 
 152             mAccountWasSet 
= true
; 
 153             mAccountWasRestored 
= (savedAccount 
|| mAccount
.equals(oldAccount
)); 
 156             swapToDefaultAccount(); 
 162      *  Tries to swap the current ownCloud {@link Account} for other valid and existing.  
 164      *  If no valid ownCloud {@link Account} exists, the the user is requested  
 165      *  to create a new ownCloud {@link Account}. 
 167      *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. 
 169      *  @return     'True' if the checked {@link Account} was valid. 
 171     private void swapToDefaultAccount() { 
 172         // default to the most recently used account 
 173         Account newAccount  
= AccountUtils
.getCurrentOwnCloudAccount(getApplicationContext()); 
 174         if (newAccount 
== null
) { 
 175             /// no account available: force account creation 
 176             createFirstAccount(); 
 177             mRedirectingToSetupAccount 
= true
; 
 178             mAccountWasSet 
= false
; 
 179             mAccountWasRestored 
= false
; 
 182             mAccountWasSet 
= true
; 
 183             mAccountWasRestored 
= (newAccount
.equals(mAccount
)); 
 184             mAccount 
= newAccount
; 
 190      * Launches the account creation activity. To use when no ownCloud account is available 
 192     private void createFirstAccount() { 
 193         AccountManager am 
= AccountManager
.get(getApplicationContext()); 
 194         am
.addAccount(MainApp
.getAccountType(),  
 199                         new AccountCreationCallback(),                         
 208     protected void onSaveInstanceState(Bundle outState
) { 
 209         super.onSaveInstanceState(outState
); 
 210         outState
.putParcelable(FileActivity
.EXTRA_FILE
, mFile
); 
 211         outState
.putParcelable(FileActivity
.EXTRA_ACCOUNT
, mAccount
); 
 212         outState
.putBoolean(FileActivity
.EXTRA_FROM_NOTIFICATION
, mFromNotification
); 
 217      * Getter for the main {@link OCFile} handled by the activity. 
 219      * @return  Main {@link OCFile} handled by the activity. 
 221     public OCFile 
getFile() { 
 227      * Setter for the main {@link OCFile} handled by the activity. 
 229      * @param file  Main {@link OCFile} to be handled by the activity. 
 231     public void setFile(OCFile file
) { 
 237      * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located. 
 239      * @return  OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. 
 241     public Account 
getAccount() { 
 246      * @return Value of mFromNotification: True if the Activity is launched by a notification 
 248     public boolean fromNotification() { 
 249         return mFromNotification
; 
 253      * @return  'True' when the Activity is finishing to enforce the setup of a new account. 
 255     protected boolean isRedirectingToSetupAccount() { 
 256         return mRedirectingToSetupAccount
; 
 261      *  @return 'True' if the server supports the Share API 
 263     public boolean isSharedSupported() { 
 264         if (getAccount() != null
) { 
 265             AccountManager accountManager 
= AccountManager
.get(this); 
 266             return Boolean
.parseBoolean(accountManager
.getUserData(getAccount(), OwnCloudAccount
.Constants
.KEY_SUPPORTS_SHARE_API
)); 
 272      * Helper class handling a callback from the {@link AccountManager} after the creation of 
 273      * a new ownCloud {@link Account} finished, successfully or not. 
 275      * At this moment, only called after the creation of the first account. 
 277      * @author David A. Velasco 
 279     public class AccountCreationCallback 
implements AccountManagerCallback
<Bundle
> { 
 282         public void run(AccountManagerFuture
<Bundle
> future
) { 
 283             FileActivity
.this.mRedirectingToSetupAccount 
= false
; 
 284             boolean accountWasSet 
= false
; 
 285             if (future 
!= null
) { 
 288                     result 
= future
.getResult(); 
 289                     String name 
= result
.getString(AccountManager
.KEY_ACCOUNT_NAME
); 
 290                     String type 
= result
.getString(AccountManager
.KEY_ACCOUNT_TYPE
); 
 291                     if (AccountUtils
.setCurrentOwnCloudAccount(getApplicationContext(), name
)) { 
 292                         setAccount(new Account(name
, type
), false
); 
 293                         accountWasSet 
= true
; 
 295                 } catch (OperationCanceledException e
) { 
 296                     Log_OC
.d(TAG
, "Account creation canceled"); 
 298                 } catch (Exception e
) { 
 299                     Log_OC
.e(TAG
, "Account creation finished in exception: ", e
); 
 303                 Log_OC
.e(TAG
, "Account creation callback with null bundle"); 
 305             if (!accountWasSet
) { 
 306                 moveTaskToBack(true
); 
 314      *  Called when the ownCloud {@link Account} associated to the Activity was just updated. 
 316      *  Child classes must grant that state depending on the {@link Account} is updated. 
 318     protected abstract void onAccountSet(boolean stateWasRecovered
); 
 322     public void openFile(OCFile file
) { 
 324             String storagePath 
= file
.getStoragePath(); 
 325             String encodedStoragePath 
= WebdavUtils
.encodePath(storagePath
); 
 327             Intent intentForSavedMimeType 
= new Intent(Intent
.ACTION_VIEW
); 
 328             intentForSavedMimeType
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), file
.getMimetype()); 
 329             intentForSavedMimeType
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 331             Intent intentForGuessedMimeType 
= null
; 
 332             if (storagePath
.lastIndexOf('.') >= 0) { 
 333                 String guessedMimeType 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(storagePath
.substring(storagePath
.lastIndexOf('.') + 1)); 
 334                 if (guessedMimeType 
!= null 
&& !guessedMimeType
.equals(file
.getMimetype())) { 
 335                     intentForGuessedMimeType 
= new Intent(Intent
.ACTION_VIEW
); 
 336                     intentForGuessedMimeType
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), guessedMimeType
); 
 337                     intentForGuessedMimeType
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 341             Intent chooserIntent 
= null
; 
 342             if (intentForGuessedMimeType 
!= null
) { 
 343                 chooserIntent 
= Intent
.createChooser(intentForGuessedMimeType
, getString(R
.string
.actionbar_open_with
)); 
 345                 chooserIntent 
= Intent
.createChooser(intentForSavedMimeType
, getString(R
.string
.actionbar_open_with
)); 
 348             startActivity(chooserIntent
); 
 351             Log_OC
.wtf(TAG
, "Trying to open a NULL OCFile"); 
 356     public void shareFileWithLink(OCFile file) { 
 359             Intent intentToShareLink = new Intent(Intent.ACTION_SEND); 
 360             intentToShareLink.putExtra(Intent.EXTRA_TEXT, "https://fake.url.lolo"); 
 361             intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE); 
 363             Intent chooserIntent = Intent.createChooser(intentToShareLink, getString(R.string.action_share_file)); 
 364             startActivity(chooserIntent); 
 367             Log_OC.wtf(TAG, "Trying to open a NULL OCFile"); 
 372     public void shareFileWithLink(OCFile file
) { 
 375             //CreateShareOperation createShare = new CreateShareOperation(file.getRemotePath(), ShareType.PUBLIC_LINK, "", false, "", 1); 
 376             //createShare.execute(getAccount(), this, this, mHandler, this); 
 378             String link 
= "https://fake.url.lolo"; 
 379             Intent chooserIntent 
= null
; 
 380             List
<Intent
> targetedShareIntents 
= new ArrayList
<Intent
>(); 
 381             List
<ResolveInfo
> resInfo 
= getPackageManager().queryIntentActivities(createShareWithLinkIntent(link
), PackageManager
.MATCH_DEFAULT_ONLY
); 
 382             String myPackageName 
= getPackageName(); 
 383             if (!resInfo
.isEmpty()) { 
 384                 for (ResolveInfo info 
: resInfo
) { 
 385                     if (!info
.activityInfo
.packageName
.equalsIgnoreCase(myPackageName
)) { 
 386                         Intent targetedShare 
= createTargetedShare(link
, info
.activityInfo
.applicationInfo
.packageName
, info
.activityInfo
.name
); 
 387                         targetedShareIntents
.add(targetedShare
); 
 391             if (targetedShareIntents
.size() > 0) { 
 392                 Intent firstTargeted 
= targetedShareIntents
.remove(0); 
 393                 chooserIntent 
= Intent
.createChooser(firstTargeted
, getString(R
.string
.action_share_file
)); 
 394                 chooserIntent
.putExtra(Intent
.EXTRA_INITIAL_INTENTS
, targetedShareIntents
.toArray(new Parcelable
[] {})); 
 396                 // to show standard message 
 397                 chooserIntent 
= Intent
.createChooser(null
, getString(R
.string
.action_share_file
)); 
 399             startActivity(chooserIntent
); 
 402             Log_OC
.wtf(TAG
, "Trying to open a NULL OCFile"); 
 406     private Intent 
createTargetedShare(String link
, String packageName
, String className
) { 
 407         //Intent targetedShare = createShareWithLinkIntent(link); 
 408         Intent targetedShare
=new Intent(Intent
.ACTION_MAIN
); 
 410         targetedShare
.addCategory(Intent
.CATEGORY_LAUNCHER
); 
 411         targetedShare
.setFlags(Intent
.FLAG_ACTIVITY_NEW_TASK 
| 
 412                     Intent
.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
);         
 413         Log_OC
.e("LOLO", "className: " + className 
+ "\npackageName: " + packageName 
+ "\n"); 
 414         targetedShare
.setClassName(packageName
, className
); 
 415         return targetedShare
; 
 419     private Intent 
createShareWithLinkIntent(String link
) { 
 420         Intent intentToShareLink 
= new Intent(Intent
.ACTION_SEND
); 
 421         intentToShareLink
.putExtra(Intent
.EXTRA_TEXT
, link
); 
 422         intentToShareLink
.setType(HTTP
.PLAIN_TEXT_TYPE
); 
 423         return intentToShareLink
;