Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -161,6 +161,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"
Expand Down
4 changes: 2 additions & 2 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,9 +36,6 @@
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;
Expand Down
24 changes: 18 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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'
Expand All @@ -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) {
Expand All @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions res/values/setup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
6 changes: 6 additions & 0 deletions res/xml/exposed_filepaths.xml
Original file line number Diff line number Diff line change
@@ -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 changes: 41 additions & 0 deletions src/com/owncloud/android/datamodel/OCFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@


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;

Expand Down Expand Up @@ -94,6 +100,14 @@ public OCFile[] newArray(int size) {
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/>
Expand Down Expand Up @@ -244,6 +258,32 @@ public Uri getStorageUri() {
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
*
Expand All @@ -252,6 +292,7 @@ public Uri getStorageUri() {
public void setStoragePath(String storage_path) {
mLocalPath = storage_path;
mLocalUri = null;
mExposedFileUri = null;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/com/owncloud/android/ui/activity/ComponentsGetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/com/owncloud/android/ui/activity/FileActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
*/

package com.owncloud.android.files;
package com.owncloud.android.ui.helpers;

import android.accounts.Account;
import android.content.ActivityNotFoundException;
Expand All @@ -40,7 +40,6 @@
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;
Expand Down Expand Up @@ -100,7 +99,6 @@ private String getUrlFromFile(String storagePath, Pattern pattern) {
}
} catch (IOException e) {
Log_OC.d(TAG, e.getMessage());
return null;
} finally {
if (br != null) {
try {
Expand Down Expand Up @@ -145,8 +143,6 @@ private Intent createIntentFromFile(String storagePath) {
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('.');
Expand All @@ -155,7 +151,10 @@ public void openFile(OCFile file) {
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
);
}
}

Expand All @@ -165,13 +164,13 @@ public void openFile(OCFile file) {

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);
Expand Down Expand Up @@ -495,11 +494,13 @@ public boolean isSearchUserSupportedSupported() {
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
Expand Down
Loading