Răsfoiți Sursa

Merge branch 'master' of https://github.com/nextcloud/android into syncedFolders

AndyScherzinger 8 ani în urmă
părinte
comite
9d7c9530b9

+ 12 - 1
AndroidManifest.xml

@@ -25,7 +25,7 @@
 
     <uses-sdk
         android:minSdkVersion="14"
-        android:targetSdkVersion="23" />
+        android:targetSdkVersion="24" />
 
     <!-- GET_ACCOUNTS is needed for API < 23.
         For API >= 23 results in the addition of CONTACTS group to the list of permissions that may be
@@ -168,6 +168,17 @@
             </intent-filter>
         </provider>
 
+        <!-- new provider used to generate URIs without file:// scheme (forbidden from Android 7) -->
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="@string/file_provider_authority"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/exposed_filepaths" />
+        </provider>
+
         <activity
             android:name=".authentication.AuthenticatorActivity"
             android:exported="true"

+ 2 - 2
SETUP.md

@@ -16,8 +16,8 @@ The [Android SDK][3] is necessary to build the app. There are different options
 Open a terminal and type 'android' to start the Android SDK Manager. To build the Nextcloud for Android app you will need to install at least the next SDK packages:
 
 * Android SDK Tools and Android SDK Platform-tools (already installed); upgrade to their last versions is usually a good idea.
-* Android SDK Build-Tools; any version from 23 or later should work fine; avoid preview versions, if any available.
-* Android 6.0 (API 23), SDK Platform; needed to build the nextcloud app.
+* Android SDK Build-Tools 24.0.2.
+* Android 7.0 (API 24), SDK Platform; needed to build the nextcloud app.
 
 Install any other package you consider interesting, such as emulators.
 

+ 4 - 3
androidTest/java/com/owncloud/android/uiautomator/InitialTest.java

@@ -18,6 +18,10 @@
 
 package com.owncloud.android.uiautomator;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -32,9 +36,6 @@ import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertThat;

+ 18 - 6
build.gradle

@@ -20,7 +20,12 @@ apply plugin: 'pmd'
 apply plugin: 'findbugs'
 
 ext {
-    supportLibraryVersion = '23.4.0'
+    supportLibraryVersion = '24.2.1'
+
+    travisBuild = System.getenv("TRAVIS") == "true"
+
+    // allows for -Dpre-dex=false to be set
+    preDexEnabled = "true".equals(System.getProperty("pre-dex", "true"))
 }
 
 repositories {
@@ -36,13 +41,14 @@ dependencies {
     /// dependencies for app building
     compile name: 'touch-image-view'
 
-    compile 'com.github.nextcloud:android-library:1.0.7'
+    compile 'com.github.nextcloud:android-library:1.0.8'
     compile "com.android.support:support-v4:${supportLibraryVersion}"
     compile "com.android.support:design:${supportLibraryVersion}"
     compile 'com.jakewharton:disklrucache:2.0.2'
     compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
     compile 'com.getbase:floatingactionbutton:1.10.1'
 
+
     /// dependencies for local unit tests
     testCompile 'junit:junit:4.12'
     testCompile 'org.mockito:mockito-core:1.10.19'
@@ -62,10 +68,10 @@ dependencies {
 
     // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests
     androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
-
     // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details
     androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
 
+
 }
 
 tasks.withType(Test) {
@@ -82,8 +88,8 @@ android {
         htmlReport true
         htmlOutput file("$project.buildDir/reports/lint/lint.html")
     }
-    compileSdkVersion 23
-    buildToolsVersion "23.0.3"
+    compileSdkVersion 24
+    buildToolsVersion "24.0.2"
 
     defaultConfig {
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -109,12 +115,14 @@ android {
             assets.srcDirs = ['assets']
         }
 
+
         // move whole local unit tests structure as a whole from src/test/* to test/*
         test.setRoot('test')
 
         // move whole instrumented tests structure as a whole from src/androidTest/* to androidTest/*
         androidTest.setRoot('androidTest')
 
+
         // Move the build types to build-types/<type>
         // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
         // This moves them out of them default location under src/<type>/... which would
@@ -124,6 +132,11 @@ android {
         debug.setRoot('build-types/debug')
         release.setRoot('build-types/release')
     }
+	
+    dexOptions {
+        // Skip pre-dexing when running on Travis CI or when disabled via -Dpre-dex=false.
+        preDexLibraries = preDexEnabled && !travisBuild
+    }
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_7
@@ -137,7 +150,6 @@ android {
     packagingOptions {
         exclude 'META-INF/LICENSE.txt'
     }
-
     task checkstyle(type: Checkstyle) {
         configFile = file("${rootProject.projectDir}/checkstyle.xml")
         configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath

+ 2 - 2
res/values/setup.xml

@@ -2,10 +2,10 @@
 <resources>
     <!-- App name  and other strings-->
     <string name="app_name">Nextcloud</string>
-    <string name="account_type">nextcloud</string>	<!-- better if was a domain name; but changing it now would require
-    migrate accounts when the app is updated -->
+    <string name="account_type">nextcloud</string>	<!-- better if was a domain name; but changing it now would require migrate accounts when the app is updated -->
     <string name="authority">org.nextcloud</string>	<!-- better if was the app package with ".provider" appended ; it identifies the provider -->
     <string name="document_provider_authority">org.nextcloud.documents</string>
+    <string name="file_provider_authority">org.nextcloud.files</string>
     <string name ="db_file">nextcloud.db</string>
     <string name ="db_name">nextcloud</string>
     <string name ="data_folder">nextcloud</string>

+ 6 - 0
res/xml/exposed_filepaths.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <external-path name="file" path="/" />
+    <!-- yes, valid for ALL external storage and not only our app folder, since we can't use @string/data_folder
+    as a value for 'path' attribute; in practice, we will only generate URIs in our folders, of course -->
+</paths>

+ 41 - 0
src/com/owncloud/android/datamodel/OCFile.java

@@ -24,10 +24,16 @@ package com.owncloud.android.datamodel;
 
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.v4.content.FileProvider;
+import android.webkit.MimeTypeMap;
 
+import com.owncloud.android.R;
+import com.owncloud.android.lib.common.network.WebdavUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.utils.MimeType;
 
@@ -94,6 +100,14 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     private Uri mLocalUri;
 
 
+    /**
+     * Exportable URI to the local path of the file contents, if stored in the device.
+     *
+     * Cached after first call, until changed.
+     */
+    private Uri mExposedFileUri;
+
+
     /**
      * Create new {@link OCFile} with given path.
      * <p/>
@@ -244,6 +258,32 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         return mLocalUri;
     }
 
+    public Uri getExposedFileUri(Context context) {
+        if (mLocalPath == null || mLocalPath.length() == 0) {
+            return null;
+        }
+        if (mExposedFileUri == null) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+                // TODO - use FileProvider with any Android version, with deeper testing -> 2.2.0
+                mExposedFileUri = Uri.parse(
+                    ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath)
+                );
+            } else {
+                // Use the FileProvider to get a content URI
+                try {
+                    mExposedFileUri = FileProvider.getUriForFile(
+                        context,
+                        context.getString(R.string.file_provider_authority),
+                        new File(mLocalPath)
+                    );
+                } catch (IllegalArgumentException e) {
+                    Log_OC.e(TAG, "File can't be exported");
+                }
+            }
+        }
+        return mExposedFileUri;
+    }
+
     /**
      * Can be used to set the path where the file is stored
      *
@@ -252,6 +292,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     public void setStoragePath(String storage_path) {
         mLocalPath = storage_path;
         mLocalUri = null;
+        mExposedFileUri = null;
     }
 
     /**

+ 1 - 1
src/com/owncloud/android/ui/activity/ComponentsGetter.java

@@ -21,7 +21,7 @@
 package com.owncloud.android.ui.activity;
 
 import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;

+ 1 - 1
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -41,7 +41,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader;

+ 1 - 1
src/com/owncloud/android/ui/activity/ManageAccountsActivity.java

@@ -50,13 +50,13 @@ import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.files.FileOperationsHelper;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.ui.adapter.AccountListAdapter;
 import com.owncloud.android.ui.adapter.AccountListItem;
+import com.owncloud.android.ui.helpers.FileOperationsHelper;
 
 import java.util.ArrayList;
 import java.util.HashSet;

+ 14 - 13
src/com/owncloud/android/files/FileOperationsHelper.java → src/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -20,7 +20,7 @@
  *
  */
 
-package com.owncloud.android.files;
+package com.owncloud.android.ui.helpers;
 
 import android.accounts.Account;
 import android.content.ActivityNotFoundException;
@@ -40,7 +40,6 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-import com.owncloud.android.lib.common.network.WebdavUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
@@ -100,7 +99,6 @@ public class FileOperationsHelper {
             }
         } catch (IOException e) {
 			Log_OC.d(TAG, e.getMessage());
-            return null;
         } finally {
             if (br != null) {
                 try {
@@ -145,8 +143,6 @@ public class FileOperationsHelper {
     public void openFile(OCFile file) {
         if (file != null) {
             String storagePath = file.getStoragePath();
-            String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-			Uri uri = Uri.parse("file://" + encodedStoragePath);
 
             Intent openFileWithIntent = null;
             int lastIndexOfDot = storagePath.lastIndexOf('.');
@@ -155,7 +151,10 @@ public class FileOperationsHelper {
                 String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExt);
                 if (guessedMimeType != null) {
                     openFileWithIntent = new Intent(Intent.ACTION_VIEW);
-                    openFileWithIntent.setDataAndType(uri, guessedMimeType);
+                    openFileWithIntent.setDataAndType(
+                            file.getExposedFileUri(mFileActivity),
+                            guessedMimeType
+                    );
                 }
             }
 
@@ -165,13 +164,13 @@ public class FileOperationsHelper {
 
             if (openFileWithIntent == null) {
                 openFileWithIntent = new Intent(Intent.ACTION_VIEW);
-                openFileWithIntent.setDataAndType(uri, file.getMimetype());
+                openFileWithIntent.setDataAndType(
+                        file.getExposedFileUri(mFileActivity),
+                        file.getMimetype()
+                );
             }
 
-            openFileWithIntent.setFlags(
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION |
-							Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-            );
+            openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
             List<ResolveInfo> launchables = mFileActivity.getPackageManager().
                     queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS);
@@ -495,11 +494,13 @@ public class FileOperationsHelper {
     public void sendDownloadedFile(OCFile file) {
         if (file != null) {
             String storagePath = file.getStoragePath();
-            String encodedStoragePath = WebdavUtils.encodePath(storagePath);
             Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
             // set MimeType
             sendIntent.setType(file.getMimetype());
-            sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + encodedStoragePath));
+            sendIntent.putExtra(
+                Intent.EXTRA_STREAM,
+                file.getExposedFileUri(mFileActivity)
+            );
             sendIntent.putExtra(Intent.ACTION_SEND, true);      // Send Action
 
             // Show dialog, without the own app

+ 5 - 18
src/com/owncloud/android/utils/FileStorageUtils.java

@@ -65,23 +65,10 @@ public class FileStorageUtils {
     public static Integer mSortOrder = SORT_NAME;
     public static Boolean mSortAscending = true;
 
-    /**
-     * Takes a full path to owncloud file and removes beginning which is path to ownload data folder.
-     * If fullPath does not start with that folder, fullPath is returned as is.
-     */
-    public static final String removeDataFolderPath(String fullPath) {
-        File sdCard = Environment.getExternalStorageDirectory();
-        String dataFolderPath = sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/";
-        if(fullPath.indexOf(dataFolderPath) == 0) {
-            return fullPath.substring(dataFolderPath.length());
-        }
-        return fullPath;
-    }
-    
     /**
      * Get local owncloud storage path for accountName.
      */
-    public static final String getSavePath(String accountName) {
+    public static String getSavePath(String accountName) {
         return MainApp.getStoragePath()
                 + File.separator
                 + MainApp.getDataFolder()
@@ -96,14 +83,14 @@ public class FileStorageUtils {
      * corresponding local path (in local owncloud storage) to remote uploaded
      * file.
      */
-    public static final String getDefaultSavePathFor(String accountName, OCFile file) {
+    public static String getDefaultSavePathFor(String accountName, OCFile file) {
         return getSavePath(accountName) + file.getRemotePath();
     }
 
     /**
      * Get absolute path to tmp folder inside datafolder in sd-card for given accountName.
      */
-    public static final String getTemporalPath(String accountName) {
+    public static String getTemporalPath(String accountName) {
         return MainApp.getStoragePath()
                 + File.separator
                 + MainApp.getDataFolder()
@@ -121,12 +108,12 @@ public class FileStorageUtils {
      * @param accountName not used. can thus be null.
      * @return Optimistic number of available bytes (can be less)
      */
-    public static final long getUsableSpace(String accountName) {
+    public static long getUsableSpace(String accountName) {
         File savePath = new File(MainApp.getStoragePath());
         return savePath.getUsableSpace();
     }
     
-    public static final String getLogPath()  {
+    public static String getLogPath()  {
         return MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator + "log";
     }
 

+ 1 - 1
tests/project.properties

@@ -11,4 +11,4 @@
 #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
 
 # Project target.
-target=android-23
+target=android-24