merge branch 'feature/purge'
|
|
@ -10,8 +10,6 @@ buildscript {
|
|||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "24.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.nononsenseapps.filepicker.examples"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 24
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':library')
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support:appcompat-v7:24.2.0'
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /home/jonas/Android/Sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.examples;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.nononsenseapps.filepicker.examples">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!-- Only needed to create sub directories. -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/FilePickerTheme">
|
||||
|
||||
<activity
|
||||
android:name=".backbutton.BackHandlingFilePickerActivity"
|
||||
android:label="Override back button"
|
||||
android:theme="@style/FilePickerTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.examples.backbutton;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class BackHandlingFilePickerActivity extends FilePickerActivity {
|
||||
|
||||
/**
|
||||
* Need access to the fragment
|
||||
*/
|
||||
BackHandlingFilePickerFragment currentFragment;
|
||||
|
||||
/**
|
||||
* Return a copy of the new fragment and set the variable above.
|
||||
*/
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(
|
||||
final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowDirCreate, final boolean allowExistingFile,
|
||||
final boolean singleClick) {
|
||||
|
||||
// startPath is allowed to be null.
|
||||
// In that case, default folder should be SD-card and not "/"
|
||||
String path = (startPath != null ? startPath
|
||||
: Environment.getExternalStorageDirectory().getPath());
|
||||
|
||||
currentFragment = new BackHandlingFilePickerFragment();
|
||||
currentFragment.setArgs(path, mode, allowMultiple, allowDirCreate,
|
||||
allowExistingFile, singleClick);
|
||||
return currentFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the back-button.
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// If at top most level, normal behaviour
|
||||
if (currentFragment.isBackTop()) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
// Else go up
|
||||
currentFragment.goUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.examples.backbutton;
|
||||
|
||||
import com.nononsenseapps.filepicker.FilePickerFragment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class BackHandlingFilePickerFragment extends FilePickerFragment {
|
||||
|
||||
/**
|
||||
* For consistency, the top level the back button checks against should be the start path.
|
||||
* But it will fall back on /.
|
||||
*/
|
||||
public File getBackTop() {
|
||||
return getPath(getArguments().getString(KEY_START_PATH, "/"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the current path is the startpath or /
|
||||
*/
|
||||
public boolean isBackTop() {
|
||||
return 0 == compareFiles(mCurrentPath, getBackTop()) ||
|
||||
0 == compareFiles(mCurrentPath, new File("/"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Go up on level, same as pressing on "..".
|
||||
*/
|
||||
public void goUp() {
|
||||
mCurrentPath = getParent(mCurrentPath);
|
||||
mCheckedItems.clear();
|
||||
mCheckedVisibleViewHolders.clear();
|
||||
refresh(mCurrentPath);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary2">#F44336</color>
|
||||
<color name="primary_dark2">#D32F2F</color>
|
||||
<color name="accent2">#FFAB00</color>
|
||||
</resources>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<resources>
|
||||
<string name="app_name">Examples</string>
|
||||
</resources>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<resources>
|
||||
|
||||
<style name="FilePickerTheme" parent="NNF_BaseTheme">
|
||||
<item name="colorPrimary">@color/primary2</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark2</item>
|
||||
<item name="colorAccent">@color/accent2</item>
|
||||
|
||||
<item name="alertDialogTheme">@style/FilePickerAlertDialogTheme</item>
|
||||
</style>
|
||||
|
||||
<style name="FilePickerAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
|
||||
<item name="colorPrimary">@color/primary2</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark2</item>
|
||||
<item name="colorAccent">@color/accent2</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.examples;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,33 +5,19 @@ apply plugin: 'com.android.library'
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// must be applied after your artifact generating plugin (eg. java / com.android.library)
|
||||
//apply plugin: 'bintray-release'
|
||||
|
||||
// query git for the the SHA, Tag and commit count. Use these to automate versioning.
|
||||
def gitTag = 'git describe --tags'.execute([], project.rootDir).text.trim()
|
||||
def gitCommitCount =
|
||||
Integer.parseInt('git rev-list --count HEAD'.execute([], project.rootDir).text.trim())
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "23.0.3"
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.2'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 23
|
||||
versionCode gitCommitCount
|
||||
versionName gitTag
|
||||
targetSdkVersion 25
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:24.2.1'
|
||||
compile 'com.android.support:support-v4:24.2.1'
|
||||
compile 'com.android.support:recyclerview-v7:24.2.1'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||
compile 'com.android.support:support-v4:25.3.1'
|
||||
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@
|
|||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:fontFamily="light"
|
||||
android:maxLines="1"
|
||||
android:gravity="center_vertical"
|
||||
android:hint="@string/nnf_name"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textAutoComplete|textAutoCorrect"
|
||||
android:singleLine="true"
|
||||
android:padding="4dp"
|
||||
tools:ignore="UnusedAttribute"/>
|
||||
</FrameLayout>
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
android:src="@drawable/nnf_ic_folder_black_48dp"
|
||||
android:tint="?attr/nnf_dir_icon_color"
|
||||
android:visibility="visible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
tools:ignore="ContentDescription,VectorDrawableCompat" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
|
|
@ -38,10 +38,10 @@
|
|||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textColor="@android:color/black"
|
||||
android:maxLines="1"
|
||||
android:padding="8dp"
|
||||
android:text="@string/nnf_name"
|
||||
android:textColor="@android:color/black"/>
|
||||
android:text="@string/nnf_name" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
android:src="@drawable/nnf_ic_folder_black_48dp"
|
||||
android:tint="?attr/nnf_dir_icon_color"
|
||||
android:visibility="visible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
tools:ignore="ContentDescription,VectorDrawableCompat"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/nnf_name"
|
||||
android:textColor="@android:color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -25,8 +25,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"/>
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
|
||||
android:maxLines="1"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
|
|
@ -94,7 +94,8 @@
|
|||
android:layout_height="48dp"
|
||||
android:hint="@android:string/ok"
|
||||
android:src="@drawable/nnf_ic_save_black_24dp"
|
||||
android:tint="?attr/nnf_save_icon_color"/>
|
||||
android:tint="?attr/nnf_save_icon_color"
|
||||
tools:ignore="VectorDrawableCompat"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
# SD-card picker
|
||||
|
||||
The sample app demonstrates the SD-card file chooser in action. No configuration is necessary for that.
|
||||
|
||||
|
||||
# Dropbox picker
|
||||
|
||||
If you want to try the Dropbox part of the sample, you'll need to get your own
|
||||
API-keys at [https://www.dropbox.com/developers/apps](https://www.dropbox.com/developers/apps)
|
||||
and insert them into DropboxSyncHelper.java and AndroidManifest.xml. See Dropbox for more details
|
||||
on that:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/spacecowboy/NoNonsense-FilePicker/master/screenshots/dropbox_app_console.png"
|
||||
</img>
|
||||
|
||||
The dropbox sample uses the Core API. If you want to use the Sync API, have look at:
|
||||
|
||||
https://github.com/spacecowboy/NotePad/tree/master/core/src/com/nononsenseapps/filepicker
|
||||
|
||||
for an example of that.
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
def gitTag = 'git describe --tags'.execute([], project.rootDir).text.trim()
|
||||
def gitCommitCount =
|
||||
Integer.parseInt('git rev-list --count HEAD'.execute([], project.rootDir).text.trim())
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/NOTICE.txt'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 18
|
||||
targetSdkVersion 23
|
||||
versionCode gitCommitCount
|
||||
versionName gitTag
|
||||
archivesBaseName = "nononsensefilepicker-sample-${gitTag}".toString()
|
||||
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
release {
|
||||
archivesBaseName = "nononsensefilepicker-sample-${gitTag}".toString()
|
||||
//minifyEnabled true
|
||||
//shrinkResources true
|
||||
//proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':library')
|
||||
|
||||
// Image loading sample
|
||||
compile 'com.github.bumptech.glide:glide:3.6.1'
|
||||
|
||||
// FTP browser sample
|
||||
compile 'commons-net:commons-net:3.3'
|
||||
|
||||
// Root example
|
||||
compile 'eu.chainfire:libsuperuser:1.0.0.+'
|
||||
|
||||
// Fast scroll example
|
||||
compile 'com.simplecityapps:recyclerview-fastscroll:1.0.9'
|
||||
|
||||
// UI Tests
|
||||
compile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||
compile 'com.android.support.test:runner:0.5'
|
||||
compile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
|
||||
compile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
|
||||
}
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
This project contains annotations derived from JCIP-ANNOTATIONS
|
||||
Copyright (c) 2005 Brian Goetz and Tim Peierls.
|
||||
See http://www.jcip.net and the Creative Commons Attribution License
|
||||
(http://creativecommons.org/licenses/by/2.5)
|
||||
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /home/jonas/android-sdk-linux/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
|
@ -1,425 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
|
||||
import android.support.test.espresso.ViewInteraction;
|
||||
import android.support.test.espresso.action.ViewActions;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.action.ViewActions.replaceText;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
||||
import static android.support.test.espresso.contrib.RecyclerViewActions.scrollTo;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static com.nononsenseapps.filepicker.sample.PermissionGranter.allowPermissionsIfNeeded;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FastScrollerNewFile {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<NoNonsenseFilePickerTest> mActivityTestRule =
|
||||
new ActivityTestRule<>(NoNonsenseFilePickerTest.class);
|
||||
|
||||
@Before
|
||||
public void allowPermissions() {
|
||||
allowPermissionsIfNeeded(mActivityTestRule.getActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectNewFile() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile), withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"),
|
||||
isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
// sub dir
|
||||
recyclerView.perform(actionOnItemAtPosition(3, click()));
|
||||
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("testfile"));
|
||||
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// Click ok
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/C-dir/testfile")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withSingleClick() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkSingleClick), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Navigate to file
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(2, click()));
|
||||
// Click file
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/B-dir/file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickTwiceShouldNotClearFilename() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"),
|
||||
isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Navigate to file
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
recyclerView.perform(actionOnItemAtPosition(2, click()));
|
||||
|
||||
// Click on file once
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Filename should be entered in field
|
||||
ViewInteraction editText = onView(withId(R.id.nnf_text_filename));
|
||||
editText.check(matches(withText("file-3.txt")));
|
||||
|
||||
// Click twice
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Filename should not change
|
||||
editText.check(matches(withText("file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithPathWhichExists() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("B-dir/file-3.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/B-dir/file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithPathWhichDoesNotExist() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("path/to/file"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/path/to/file")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithDotDot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("../file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/../file.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithDot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("./file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/./file.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithRoot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
onView(withId(R.id.button_fastscroll)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_fastscroll), withText("Pick With Fast Scroller"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("/file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///file.txt")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
|
||||
import android.support.test.espresso.ViewInteraction;
|
||||
import android.support.test.espresso.action.ViewActions;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.action.ViewActions.replaceText;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static com.nononsenseapps.filepicker.sample.PermissionGranter.allowPermissionsIfNeeded;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FtpPicker {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<NoNonsenseFilePickerTest> mActivityTestRule =
|
||||
new ActivityTestRule<>(NoNonsenseFilePickerTest.class);
|
||||
|
||||
@Before
|
||||
public void allowPermissions() {
|
||||
allowPermissionsIfNeeded(mActivityTestRule.getActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectDir() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioDir), withText("Select directory"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
onView(withId(R.id.button_ftp)).perform(ViewActions.scrollTo());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_ftp), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// press pub
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
ViewInteraction okButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok),
|
||||
withParent(allOf(withId(R.id.nnf_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// Click ok
|
||||
okButton.perform(click());
|
||||
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("ftp://anonymous:anonymous@debian.simnet.is:21/pub")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.support.test.uiautomator.UiObjectNotFoundException;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
public class PermissionGranter {
|
||||
|
||||
private static final int PERMISSIONS_DIALOG_DELAY = 3000;
|
||||
private static final int GRANT_BUTTON_INDEX = 1;
|
||||
|
||||
public static void allowPermissionsIfNeeded(Activity activity) {
|
||||
allowPermissionsIfNeeded(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
public static void allowPermissionsIfNeeded(Activity activity, String permissionNeeded) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasNeededPermission(activity, permissionNeeded)) {
|
||||
sleep(PERMISSIONS_DIALOG_DELAY);
|
||||
UiDevice device = UiDevice.getInstance(getInstrumentation());
|
||||
UiObject allowPermissions = device.findObject(new UiSelector().clickable(true).index(GRANT_BUTTON_INDEX));
|
||||
if (allowPermissions.exists()) {
|
||||
allowPermissions.click();
|
||||
}
|
||||
}
|
||||
} catch (UiObjectNotFoundException e) {
|
||||
System.out.println("There is no permissions dialog to interact with");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasNeededPermission(Activity activity, String permissionNeeded) {
|
||||
int permissionStatus = ContextCompat.checkSelfPermission(activity, permissionNeeded);
|
||||
return permissionStatus == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Cannot execute Thread.sleep()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,406 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
|
||||
import android.support.test.espresso.ViewInteraction;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
||||
import static android.support.test.espresso.action.ViewActions.*;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.*;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.*;
|
||||
|
||||
import com.nononsenseapps.filepicker.sample.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.nononsenseapps.filepicker.sample.PermissionGranter.allowPermissionsIfNeeded;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SelectNewFile {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<NoNonsenseFilePickerTest> mActivityTestRule =
|
||||
new ActivityTestRule<>(NoNonsenseFilePickerTest.class);
|
||||
|
||||
@Before
|
||||
public void allowPermissions() {
|
||||
allowPermissionsIfNeeded(mActivityTestRule.getActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectNewFile() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile), withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
// sub dir
|
||||
recyclerView.perform(actionOnItemAtPosition(3, click()));
|
||||
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("testfile"));
|
||||
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// Click ok
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/C-dir/testfile")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withSingleClick() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkSingleClick), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Navigate to file
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(2, click()));
|
||||
// Click file
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/B-dir/file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickTwiceShouldNotClearFilename() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Navigate to file
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
recyclerView.perform(actionOnItemAtPosition(2, click()));
|
||||
|
||||
// Click on file once
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Filename should be entered in field
|
||||
ViewInteraction editText = onView(withId(R.id.nnf_text_filename));
|
||||
editText.check(matches(withText("file-3.txt")));
|
||||
|
||||
// Click twice
|
||||
recyclerView.perform(actionOnItemAtPosition(4, click()));
|
||||
|
||||
// Filename should not change
|
||||
editText.check(matches(withText("file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithPathWhichExists() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("B-dir/file-3.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/B-dir/file-3.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithPathWhichDoesNotExist() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("path/to/file"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/path/to/file")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithDotDot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("../file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/../file.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithDot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("./file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/./file.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterFileNameWithRoot() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction checkBox = onView(
|
||||
allOf(withId(R.id.checkAllowExistingFile),
|
||||
withText("Allow selection of existing (new) file"), isDisplayed()));
|
||||
checkBox.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction recyclerView = onView(
|
||||
allOf(withId(android.R.id.list), isDisplayed()));
|
||||
|
||||
// Refresh view (into dir, and out again)
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
recyclerView.perform(actionOnItemAtPosition(0, click()));
|
||||
|
||||
// Click on test dir
|
||||
recyclerView.perform(actionOnItemAtPosition(1, click()));
|
||||
|
||||
// Enter path in filename
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// new file name
|
||||
appCompatEditText.perform(replaceText("/file.txt"));
|
||||
|
||||
// Click ok
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
// Should have returned
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///file.txt")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Environment;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.espresso.ViewInteraction;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static com.nononsenseapps.filepicker.sample.PermissionGranter.allowPermissionsIfNeeded;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
/**
|
||||
* In this class, the activity is launched using an intent pointing to a file.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SelectNewFileStartPathIsFile {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<NoNonsenseFilePickerTest> mActivityTestRule =
|
||||
new ActivityTestRule<NoNonsenseFilePickerTest>(NoNonsenseFilePickerTest.class) {
|
||||
@Override
|
||||
protected Intent getActivityIntent() {
|
||||
Context targetContext = InstrumentationRegistry.getInstrumentation()
|
||||
.getTargetContext();
|
||||
Intent result = new Intent(targetContext, NoNonsenseFilePickerTest.class);
|
||||
String path = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(),
|
||||
"000000_nonsense-tests/A-dir/file-3.txt").getAbsolutePath();
|
||||
result.putExtra(AbstractFilePickerActivity.EXTRA_START_PATH, path);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@Before
|
||||
public void allowPermissions() {
|
||||
allowPermissionsIfNeeded(mActivityTestRule.getActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectNewFileWithStartPath() throws IOException {
|
||||
ViewInteraction radioButton = onView(
|
||||
allOf(withId(R.id.radioNewFile), withText("Select new file"),
|
||||
withParent(withId(R.id.radioGroup)),
|
||||
isDisplayed()));
|
||||
radioButton.perform(click());
|
||||
|
||||
ViewInteraction button = onView(
|
||||
allOf(withId(R.id.button_sd), withText("Pick SD-card"), isDisplayed()));
|
||||
button.perform(click());
|
||||
|
||||
ViewInteraction appCompatEditText = onView(
|
||||
allOf(withId(R.id.nnf_text_filename),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
|
||||
appCompatEditText.check(matches(withText("file-3.txt")));
|
||||
|
||||
ViewInteraction appCompatImageButton = onView(
|
||||
allOf(withId(R.id.nnf_button_ok_newfile),
|
||||
withParent(allOf(withId(R.id.nnf_newfile_button_container),
|
||||
withParent(withId(R.id.nnf_buttons_container)))),
|
||||
isDisplayed()));
|
||||
// Click ok
|
||||
appCompatImageButton.perform(click());
|
||||
|
||||
ViewInteraction textView = onView(withId(R.id.text));
|
||||
textView.check(matches(withText("file:///storage/emulated/0/000000_nonsense-tests/A-dir/file-3.txt")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.nononsenseapps.filepicker.sample">
|
||||
|
||||
<!-- For SD-card -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- For Dropbox -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme"
|
||||
tools:replace="android:label">
|
||||
|
||||
<!-- Sample app -->
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.NoNonsenseFilePicker"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Sample test app -->
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.NoNonsenseFilePickerTest"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"/>
|
||||
|
||||
<!-- SD-card pickers -->
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.FilePickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.FilePickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".multimedia.MultimediaPickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".multimedia.MultimediaPickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".root.SUPickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".root.SUPickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".fastscroller.FastScrollerFilePickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".fastscroller.FastScrollerFilePickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- FtpPickers -->
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme" />
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight" />
|
||||
|
||||
<!-- Dropbox pickers -->
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleTheme">
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity2"
|
||||
android:label="@string/title_activity_no_nonsense_file_picker"
|
||||
android:theme="@style/SampleThemeLight">
|
||||
</activity>
|
||||
<!-- Dropbox authenticator -->
|
||||
<activity
|
||||
android:name="com.dropbox.client2.android.AuthActivity"
|
||||
android:configChanges="orientation|keyboard"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<!-- Change this to be db- followed by your app key -->
|
||||
<data android:scheme="db-sm57t7s6lmgj745" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
|
||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||
|
||||
/**
|
||||
* This is a copy of the included activity for the sole purpose
|
||||
* of being to show you a second example theme
|
||||
*/
|
||||
public class FilePickerActivity2 extends FilePickerActivity {
|
||||
}
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxSyncHelper;
|
||||
import com.nononsenseapps.filepicker.sample.fastscroller.FastScrollerFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.fastscroller.FastScrollerFilePickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.multimedia.MultimediaPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.multimedia.MultimediaPickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.root.SUPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.root.SUPickerActivity2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public class NoNonsenseFilePicker extends Activity {
|
||||
|
||||
static final int CODE_SD = 0;
|
||||
static final int CODE_DB = 1;
|
||||
static final int CODE_FTP = 2;
|
||||
TextView textView;
|
||||
DropboxAPI<AndroidAuthSession> mDBApi = null;
|
||||
CheckBox checkAllowCreateDir;
|
||||
CheckBox checkAllowMultiple;
|
||||
CheckBox checkSingleClick;
|
||||
CheckBox checkLightTheme;
|
||||
RadioGroup radioGroup;
|
||||
CheckBox checkAllowExistingFile;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_no_nonsense_file_picker);
|
||||
|
||||
checkAllowCreateDir =
|
||||
(CheckBox) findViewById(R.id.checkAllowCreateDir);
|
||||
checkAllowMultiple =
|
||||
(CheckBox) findViewById(R.id.checkAllowMultiple);
|
||||
checkAllowExistingFile =
|
||||
(CheckBox) findViewById(R.id.checkAllowExistingFile);
|
||||
checkSingleClick =
|
||||
(CheckBox) findViewById(R.id.checkSingleClick);
|
||||
checkLightTheme =
|
||||
(CheckBox) findViewById(R.id.checkLightTheme);
|
||||
radioGroup =
|
||||
(RadioGroup) findViewById(R.id.radioGroup);
|
||||
textView = (TextView) findViewById(R.id.text);
|
||||
|
||||
findViewById(R.id.button_sd)
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_SD, FilePickerActivity2.class);
|
||||
} else {
|
||||
startActivity(CODE_SD, FilePickerActivity.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button_image)
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_SD, MultimediaPickerActivity2.class);
|
||||
} else {
|
||||
startActivity(CODE_SD, MultimediaPickerActivity.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button_ftp)
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_FTP, FtpPickerActivity2.class);
|
||||
} else {
|
||||
startActivity(CODE_FTP, FtpPickerActivity.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button_dropbox)
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
|
||||
// First we must authorize the user
|
||||
if (mDBApi == null) {
|
||||
mDBApi = DropboxSyncHelper
|
||||
.getDBApi(NoNonsenseFilePicker.this);
|
||||
}
|
||||
|
||||
// If not authorized, then ask user for login/permission
|
||||
if (!mDBApi.getSession().isLinked()) {
|
||||
mDBApi.getSession().startOAuth2Authentication(
|
||||
NoNonsenseFilePicker.this);
|
||||
} else { // User is authorized, open file picker
|
||||
Intent i;
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_DB, DropboxFilePickerActivity2.class);
|
||||
} else {
|
||||
startActivity(CODE_DB, DropboxFilePickerActivity.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button_root).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_SD, SUPickerActivity.class);
|
||||
} else {
|
||||
startActivity(CODE_SD, SUPickerActivity2.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.button_fastscroll).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (checkLightTheme.isChecked()) {
|
||||
startActivity(CODE_SD, FastScrollerFilePickerActivity.class);
|
||||
} else {
|
||||
startActivity(CODE_SD, FastScrollerFilePickerActivity2.class);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void startActivity(final int code, final Class<?> klass) {
|
||||
final Intent i = new Intent(this, klass);
|
||||
|
||||
i.setAction(Intent.ACTION_GET_CONTENT);
|
||||
|
||||
i.putExtra(SUPickerActivity.EXTRA_ALLOW_MULTIPLE,
|
||||
checkAllowMultiple.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_SINGLE_CLICK,
|
||||
checkSingleClick.isChecked());
|
||||
i.putExtra(SUPickerActivity.EXTRA_ALLOW_CREATE_DIR,
|
||||
checkAllowCreateDir.isChecked());
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_EXISTING_FILE,
|
||||
checkAllowExistingFile.isChecked());
|
||||
|
||||
// What mode is selected
|
||||
final int mode;
|
||||
switch (radioGroup.getCheckedRadioButtonId()) {
|
||||
case R.id.radioDir:
|
||||
mode = AbstractFilePickerFragment.MODE_DIR;
|
||||
break;
|
||||
case R.id.radioFilesAndDirs:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE_AND_DIR;
|
||||
break;
|
||||
case R.id.radioNewFile:
|
||||
mode = AbstractFilePickerFragment.MODE_NEW_FILE;
|
||||
break;
|
||||
case R.id.radioFile:
|
||||
default:
|
||||
mode = AbstractFilePickerFragment.MODE_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
i.putExtra(FilePickerActivity.EXTRA_MODE, mode);
|
||||
|
||||
// This line is solely so that test classes can override intents given through UI
|
||||
i.putExtras(getIntent());
|
||||
|
||||
startActivityForResult(i, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is entirely for Dropbox's benefit
|
||||
*/
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mDBApi != null && mDBApi.getSession().authenticationSuccessful()) {
|
||||
try {
|
||||
// Required to complete auth, sets the access token on the session
|
||||
mDBApi.getSession().finishAuthentication();
|
||||
|
||||
String accessToken = mDBApi.getSession().getOAuth2AccessToken();
|
||||
DropboxSyncHelper.saveToken(this, accessToken);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.i("DbAuthLog", "Error authenticating", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.no_nonsense_file_picker, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
int id = item.getItemId();
|
||||
return id == R.id.action_settings || super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
if ((CODE_SD == requestCode || CODE_DB == requestCode || CODE_FTP == requestCode) &&
|
||||
resultCode == Activity.RESULT_OK) {
|
||||
if (data.getBooleanExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE,
|
||||
false)) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
ClipData clip = data.getClipData();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (clip != null) {
|
||||
for (int i = 0; i < clip.getItemCount(); i++) {
|
||||
sb.append(clip.getItemAt(i).getUri().toString());
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
textView.setText(sb.toString());
|
||||
} else {
|
||||
ArrayList<String> paths = data.getStringArrayListExtra(
|
||||
FilePickerActivity.EXTRA_PATHS);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (paths != null) {
|
||||
for (String path : paths) {
|
||||
sb.append(path);
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
textView.setText(sb.toString());
|
||||
}
|
||||
} else {
|
||||
textView.setText(data.getData().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxFilePickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.dropbox.DropboxSyncHelper;
|
||||
import com.nononsenseapps.filepicker.sample.fastscroller.FastScrollerFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.fastscroller.FastScrollerFilePickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.ftp.FtpPickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.multimedia.MultimediaPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.multimedia.MultimediaPickerActivity2;
|
||||
import com.nononsenseapps.filepicker.sample.root.SUPickerActivity;
|
||||
import com.nononsenseapps.filepicker.sample.root.SUPickerActivity2;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class NoNonsenseFilePickerTest extends NoNonsenseFilePicker {
|
||||
private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Request permission
|
||||
if (hasPermission()) {
|
||||
try {
|
||||
createTestData();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
}
|
||||
|
||||
void createTestData() throws IOException {
|
||||
File sdRoot = Environment.getExternalStorageDirectory().getAbsoluteFile();
|
||||
|
||||
File testRoot = new File(sdRoot, "000000_nonsense-tests");
|
||||
|
||||
testRoot.mkdir();
|
||||
assertTrue("Failed to create directory", testRoot.isDirectory());
|
||||
|
||||
List<File> subdirs = Arrays.asList(new File(testRoot, "A-dir"),
|
||||
new File(testRoot, "B-dir"),
|
||||
new File(testRoot, "C-dir"));
|
||||
|
||||
|
||||
for (File subdir : subdirs) {
|
||||
subdir.mkdir();
|
||||
assertTrue("Failed to create sub directory", subdir.isDirectory());
|
||||
|
||||
for (int sf = 0; sf < 10; sf++) {
|
||||
File subfile = new File(subdir, "file-" + sf + ".txt");
|
||||
|
||||
subfile.createNewFile();
|
||||
|
||||
assertTrue("Failed to create file", subfile.isFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasPermission() {
|
||||
return PackageManager.PERMISSION_GRANTED ==
|
||||
ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
protected void requestPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
// If arrays are empty, then process was cancelled
|
||||
if (permissions.length > 0) {
|
||||
if (PackageManager.PERMISSION_GRANTED == grantResults[0]) {
|
||||
try {
|
||||
createTestData();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.dropbox;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
||||
|
||||
public class DropboxFilePickerActivity
|
||||
extends AbstractFilePickerActivity<DropboxAPI.Entry> {
|
||||
|
||||
// In the class declaration section:
|
||||
private DropboxAPI<AndroidAuthSession> mDBApi;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle b) {
|
||||
mDBApi = DropboxSyncHelper.getDBApi(this);
|
||||
if (!mDBApi.getSession().isLinked()) {
|
||||
// No valid authentication
|
||||
finish();
|
||||
}
|
||||
|
||||
super.onCreate(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<DropboxAPI.Entry> getFragment(
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir, final boolean allowExistingFile,
|
||||
final boolean singleClick) {
|
||||
if (mDBApi == null || !mDBApi.getSession().isLinked()) {
|
||||
// No valid authentication
|
||||
finish();
|
||||
return null;
|
||||
}
|
||||
|
||||
DropboxFilePickerFragment fragment =
|
||||
new DropboxFilePickerFragment(mDBApi);
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir,
|
||||
allowExistingFile, singleClick);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.dropbox;
|
||||
|
||||
/**
|
||||
* This is a copy of the included activity for the sole purpose
|
||||
* of being to show you a second example theme
|
||||
*/
|
||||
public class DropboxFilePickerActivity2 extends DropboxFilePickerActivity {
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.dropbox;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.util.SortedList;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v7.widget.util.SortedListAdapterCallback;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
import com.dropbox.client2.exception.DropboxException;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.FileItemAdapter;
|
||||
import com.nononsenseapps.filepicker.sample.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
public class DropboxFilePickerFragment
|
||||
extends AbstractFilePickerFragment<DropboxAPI.Entry> {
|
||||
|
||||
private final DropboxAPI<AndroidAuthSession> dbApi;
|
||||
private ProgressBar progressBar;
|
||||
private RecyclerView recyclerView;
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
public DropboxFilePickerFragment(final DropboxAPI<AndroidAuthSession> api) {
|
||||
super();
|
||||
if (api == null) {
|
||||
throw new NullPointerException("FileSystem may not be null");
|
||||
} else if (!api.getSession().isLinked()) {
|
||||
throw new IllegalArgumentException("Must be linked with Dropbox");
|
||||
}
|
||||
|
||||
this.dbApi = api;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View inflateRootView(LayoutInflater inflater, ViewGroup container) {
|
||||
// Load the specific layout we created for dropbox/ftp
|
||||
View view = inflater.inflate(R.layout.fragment_loading_filepicker, container, false);
|
||||
// And bind the progress bar
|
||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are loading, then hide the list and show the progress bar instead.
|
||||
*
|
||||
* @param nextPath path to list files for
|
||||
*/
|
||||
@Override
|
||||
protected void refresh(DropboxAPI.Entry nextPath) {
|
||||
super.refresh(nextPath);
|
||||
if (isLoading) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Once loading has finished, show the list and hide the progress bar.
|
||||
*/
|
||||
@Override
|
||||
public void onLoadFinished(Loader<SortedList<DropboxAPI.Entry>> loader, SortedList<DropboxAPI
|
||||
.Entry> data) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
super.onLoadFinished(loader, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Once loading has finished, show the list and hide the progress bar.
|
||||
*/
|
||||
@Override
|
||||
public void onLoaderReset(Loader<SortedList<DropboxAPI.Entry>> loader) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
super.onLoaderReset(loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewFolder(@NonNull final String name) {
|
||||
File folder = new File(mCurrentPath.path, name);
|
||||
new FolderCreator().execute(folder.getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDir(@NonNull final DropboxAPI.Entry file) {
|
||||
return file.isDir;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DropboxAPI.Entry getParent(@NonNull final DropboxAPI.Entry from) {
|
||||
// Take care of a slight limitation in Dropbox code:
|
||||
if (from.path.length() > 1 && from.path.endsWith("/")) {
|
||||
from.path = from.path.substring(0, from.path.length() - 1);
|
||||
}
|
||||
String parent = from.parentPath();
|
||||
if (TextUtils.isEmpty(parent)) {
|
||||
parent = "/";
|
||||
}
|
||||
|
||||
return getPath(parent);
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DropboxAPI.Entry getPath(@NonNull final String path) {
|
||||
final DropboxAPI.Entry entry = new DropboxAPI.Entry();
|
||||
entry.path = path;
|
||||
entry.isDir = true;
|
||||
return entry;
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFullPath(@NonNull final DropboxAPI.Entry file) {
|
||||
return file.path;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName(@NonNull final DropboxAPI.Entry file) {
|
||||
return file.fileName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DropboxAPI.Entry getRoot() {
|
||||
return getPath("/");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Uri toUri(@NonNull final DropboxAPI.Entry file) {
|
||||
return new Uri.Builder().scheme("dropbox").authority("").path(file.path).build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<SortedList<DropboxAPI.Entry>> getLoader() {
|
||||
return new AsyncTaskLoader<SortedList<DropboxAPI.Entry>>(getActivity()) {
|
||||
|
||||
@Override
|
||||
public SortedList<DropboxAPI.Entry> loadInBackground() {
|
||||
SortedList<DropboxAPI.Entry> files = new SortedList<>(DropboxAPI.Entry.class,
|
||||
new SortedListAdapterCallback<DropboxAPI.Entry>(null) {
|
||||
@Override
|
||||
public int compare(DropboxAPI.Entry lhs, DropboxAPI.Entry rhs) {
|
||||
if (isDir(lhs) && !isDir(rhs)) {
|
||||
return -1;
|
||||
} else if (isDir(rhs) && !isDir(lhs)) {
|
||||
return 1;
|
||||
} else {
|
||||
return lhs.fileName().toLowerCase()
|
||||
.compareTo(rhs.fileName().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInserted(int position, int count) {
|
||||
// Ignore (DO NOT MODIFY ADAPTER HERE!)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(int position, int count) {
|
||||
// Ignore (DO NOT MODIFY ADAPTER HERE!)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoved(int fromPosition, int toPosition) {
|
||||
// Ignore (DO NOT MODIFY ADAPTER HERE!)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(int position, int count) {
|
||||
// Ignore (DO NOT MODIFY ADAPTER HERE!)
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(DropboxAPI.Entry lhs, DropboxAPI.Entry rhs) {
|
||||
return lhs.fileName().equals(rhs.fileName()) && (lhs.isDir == rhs.isDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(DropboxAPI.Entry lhs, DropboxAPI.Entry rhs) {
|
||||
return areContentsTheSame(lhs, rhs);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
try {
|
||||
|
||||
if (!dbApi.metadata(mCurrentPath.path, 1, null, false,
|
||||
null).isDir) {
|
||||
mCurrentPath = getRoot();
|
||||
}
|
||||
|
||||
DropboxAPI.Entry dirEntry =
|
||||
dbApi.metadata(mCurrentPath.path, 0, null, true,
|
||||
null);
|
||||
|
||||
files.beginBatchedUpdates();
|
||||
|
||||
for (DropboxAPI.Entry entry : dirEntry.contents) {
|
||||
if ((mode == MODE_FILE || mode == MODE_FILE_AND_DIR) ||
|
||||
entry.isDir) {
|
||||
files.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
files.endBatchedUpdates();
|
||||
} catch (DropboxException ignored) {
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to start the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
super.onStartLoading();
|
||||
|
||||
if (mCurrentPath == null || !mCurrentPath.isDir) {
|
||||
mCurrentPath = getRoot();
|
||||
}
|
||||
|
||||
forceLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to completely reset the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dropbox requires stuff to be done in a background thread. Refreshing has to be done on the
|
||||
* UI thread however (it restarts the loader so actual work is done in the background).
|
||||
*/
|
||||
private class FolderCreator extends AsyncTask<String, Void, DropboxAPI.Entry> {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
// Switch to progress bar before starting work
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DropboxAPI.Entry doInBackground(final String... paths) {
|
||||
if (paths.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String path = paths[0];
|
||||
try {
|
||||
dbApi.createFolder(path);
|
||||
return dbApi.metadata(path, 1, null, false, null);
|
||||
} catch (DropboxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(@Nullable DropboxAPI.Entry path) {
|
||||
if (path != null) {
|
||||
goToDir(path);
|
||||
} else {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
Toast.makeText(getActivity(), R.string.nnf_create_folder_error,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.dropbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.dropbox.client2.DropboxAPI;
|
||||
import com.dropbox.client2.android.AndroidAuthSession;
|
||||
import com.dropbox.client2.session.AppKeyPair;
|
||||
|
||||
/**
|
||||
* This class has some utility functions for dealing with Dropbox. You need
|
||||
* to input your API keys below.
|
||||
* See Dropbox for more information:
|
||||
* https://www.dropbox.com/developers/core/start/android
|
||||
* <p/>
|
||||
* You also need to drop your APP_KEY in the manifest in
|
||||
* com.dropbox.client2.android.AuthActivity
|
||||
* See here for info:
|
||||
* https://www.dropbox.com/developers/core/sdks/android
|
||||
*/
|
||||
public class DropboxSyncHelper {
|
||||
// Change these two lines to your app's stuff
|
||||
final static public String APP_KEY = "sm57t7s6lmgj745";
|
||||
final static public String APP_SECRET = "eie6mq0lvcw9t7x";
|
||||
|
||||
public static final String PREF_DROPBOX_TOKEN = "dropboxtoken";
|
||||
|
||||
public static DropboxAPI<AndroidAuthSession> getDBApi(
|
||||
final Context context) {
|
||||
final DropboxAPI<AndroidAuthSession> mDBApi;
|
||||
|
||||
final AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
|
||||
final AndroidAuthSession session;
|
||||
|
||||
if (PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.contains(PREF_DROPBOX_TOKEN)) {
|
||||
session = new AndroidAuthSession(appKeys,
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(PREF_DROPBOX_TOKEN, ""));
|
||||
} else {
|
||||
session = new AndroidAuthSession(appKeys);
|
||||
}
|
||||
mDBApi = new DropboxAPI<AndroidAuthSession>(session);
|
||||
return mDBApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the dropbox oauth token so we can reuse the session without
|
||||
* logging in again.
|
||||
* @param context
|
||||
* @param token
|
||||
*/
|
||||
public static void saveToken(final Context context, final String token) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putString(PREF_DROPBOX_TOKEN, token).apply();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.fastscroller;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.nononsenseapps.filepicker.FileItemAdapter;
|
||||
import com.nononsenseapps.filepicker.LogicHandler;
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class FastScrollerFileItemAdapter extends FileItemAdapter<File> implements
|
||||
FastScrollRecyclerView.SectionedAdapter {
|
||||
|
||||
public FastScrollerFileItemAdapter(
|
||||
@NonNull LogicHandler<File> logic) {
|
||||
super(logic);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getSectionName(int position) {
|
||||
File path = getItem(position);
|
||||
if (path == null) {
|
||||
return "..";
|
||||
}
|
||||
return mLogic.getName(path).substring(0, 1).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.fastscroller;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* All this class does is return a suitable fragment.
|
||||
*/
|
||||
public class FastScrollerFilePickerActivity extends AbstractFilePickerActivity {
|
||||
|
||||
public FastScrollerFilePickerActivity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir, final boolean allowExistingFile,
|
||||
final boolean singleClick) {
|
||||
AbstractFilePickerFragment<File> fragment = new FastScrollerFilePickerFragment();
|
||||
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
|
||||
fragment.setArgs(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
||||
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.fastscroller;
|
||||
|
||||
/**
|
||||
* Just for theme sample purposes
|
||||
*/
|
||||
public class FastScrollerFilePickerActivity2 extends FastScrollerFilePickerActivity {
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.fastscroller;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.nononsenseapps.filepicker.FilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.sample.R;
|
||||
|
||||
public class FastScrollerFilePickerFragment extends FilePickerFragment {
|
||||
@Override
|
||||
protected View inflateRootView(LayoutInflater inflater, ViewGroup container) {
|
||||
return inflater.inflate(R.layout.fragment_fastscrollerfilepicker, container, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
/**
|
||||
* Adds path information to FtpFile objects
|
||||
*/
|
||||
public class FTPPath {
|
||||
public final String path;
|
||||
public final FTPFile file;
|
||||
|
||||
public FTPPath(@NonNull String path, @NonNull FTPFile file) {
|
||||
this.path = path;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public FTPPath(@NonNull FTPPath mCurrentPath, @NonNull FTPFile file) {
|
||||
this.file = file;
|
||||
if (mCurrentPath.path.endsWith("/")) {
|
||||
this.path = mCurrentPath + file.getName();
|
||||
} else {
|
||||
this.path = mCurrentPath.path + "/" + file.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return file.isDirectory();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
public String appendToDir(@NonNull String name) {
|
||||
if (this.path.endsWith("/")) {
|
||||
return path + name;
|
||||
} else {
|
||||
return path + "/" + name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
public class FtpDir extends FtpFile {
|
||||
|
||||
public FtpDir(FtpFile dir, String name) {
|
||||
super(dir, name);
|
||||
}
|
||||
|
||||
public FtpDir(String path) {
|
||||
super(path);
|
||||
}
|
||||
|
||||
public FtpDir(String dirPath, String name) {
|
||||
super(dirPath, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
public class FtpFile {
|
||||
|
||||
public static final char separatorChar = '/';
|
||||
public static final String separator = "/";
|
||||
private String path;
|
||||
|
||||
public FtpFile(FtpFile dir, String name) {
|
||||
this(dir == null ? null : dir.getPath(), name);
|
||||
}
|
||||
|
||||
public FtpFile(String path) {
|
||||
this.path = fixSlashes(path);
|
||||
}
|
||||
|
||||
public FtpFile(String dirPath, String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name == null");
|
||||
}
|
||||
if (dirPath == null || dirPath.isEmpty()) {
|
||||
this.path = fixSlashes(name);
|
||||
} else if (name.isEmpty()) {
|
||||
this.path = fixSlashes(dirPath);
|
||||
} else {
|
||||
this.path = fixSlashes(join(dirPath, name));
|
||||
}
|
||||
}
|
||||
|
||||
public static String fixSlashes(String origPath) {
|
||||
// Remove duplicate adjacent slashes.
|
||||
boolean lastWasSlash = false;
|
||||
char[] newPath = origPath.toCharArray();
|
||||
int length = newPath.length;
|
||||
int newLength = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char ch = newPath[i];
|
||||
if (ch == '/') {
|
||||
if (!lastWasSlash) {
|
||||
newPath[newLength++] = separatorChar;
|
||||
lastWasSlash = true;
|
||||
}
|
||||
} else {
|
||||
newPath[newLength++] = ch;
|
||||
lastWasSlash = false;
|
||||
}
|
||||
}
|
||||
// Remove any trailing slash (unless this is the root of the file system).
|
||||
if (lastWasSlash && newLength > 1) {
|
||||
newLength--;
|
||||
}
|
||||
// Reuse the original string if possible.
|
||||
return (newLength != length) ? new String(newPath, 0, newLength) : origPath;
|
||||
}
|
||||
|
||||
// Joins two path components, adding a separator only if necessary.
|
||||
public static String join(String prefix, String suffix) {
|
||||
int prefixLength = prefix.length();
|
||||
boolean haveSlash = (prefixLength > 0 && prefix.charAt(prefixLength - 1) == separatorChar);
|
||||
if (!haveSlash) {
|
||||
haveSlash = (suffix.length() > 0 && suffix.charAt(0) == separatorChar);
|
||||
}
|
||||
return haveSlash ? (prefix + suffix) : (prefix + separatorChar + suffix);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
int separatorIndex = path.lastIndexOf(separator);
|
||||
return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
int length = path.length(), firstInPath = 0;
|
||||
int index = path.lastIndexOf(separatorChar);
|
||||
if (index == -1 || path.charAt(length - 1) == separatorChar) {
|
||||
return null;
|
||||
}
|
||||
if (path.indexOf(separatorChar) == index
|
||||
&& path.charAt(firstInPath) == separatorChar) {
|
||||
return path.substring(0, index + 1);
|
||||
}
|
||||
return path.substring(0, index);
|
||||
}
|
||||
|
||||
public FtpFile getParentFile() {
|
||||
String tempParent = getParent();
|
||||
if (tempParent == null) {
|
||||
return null;
|
||||
}
|
||||
return new FtpFile(tempParent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of this file.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFile() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
|
||||
/**
|
||||
* An example implementation of an FTP file-picker
|
||||
*/
|
||||
public class FtpPickerActivity extends AbstractFilePickerActivity<FtpFile> {
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<FtpFile> getFragment(@Nullable String startPath, int mode,
|
||||
boolean allowMultiple,
|
||||
boolean allowCreateDir,
|
||||
boolean allowExistingFile,
|
||||
boolean singleClick) {
|
||||
return FtpPickerFragment.newInstance(startPath, mode, allowMultiple, allowCreateDir,
|
||||
allowExistingFile, singleClick,
|
||||
"debian.simnet.is",
|
||||
FTPClient.DEFAULT_PORT,
|
||||
null,
|
||||
null, "/");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
|
||||
/**
|
||||
* This is a copy of the included activity for the sole purpose
|
||||
* of being to show you a second example theme
|
||||
*/
|
||||
public class FtpPickerActivity2 extends FtpPickerActivity {
|
||||
}
|
||||
|
|
@ -1,389 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.ftp;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.util.SortedList;
|
||||
import android.support.v7.widget.util.SortedListAdapterCallback;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.sample.R;
|
||||
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This example allows you to browse the files on an FTP-server
|
||||
*/
|
||||
public class FtpPickerFragment extends AbstractFilePickerFragment<FtpFile> {
|
||||
|
||||
private static final String KEY_FTP_SERVER = "KEY_FTP_SERVER";
|
||||
private static final String KEY_FTP_PORT = "KEY_FTP_PORT";
|
||||
private static final String KEY_FTP_USERNAME = "KEY_FTP_USERNAME";
|
||||
private static final String KEY_FTP_PASSWORD = "KEY_FTP_PASSWORD";
|
||||
private static final String KEY_FTP_ROOTDIR = "KEY_FTP_ROOTDIR";
|
||||
private static final String TAG = "NoNonsenseFtp";
|
||||
private final FTPClient ftp;
|
||||
private String server;
|
||||
private int port;
|
||||
private String username;
|
||||
private String password;
|
||||
private boolean loggedIn = false;
|
||||
private String rootDir = "/";
|
||||
private ProgressBar progressBar;
|
||||
|
||||
public FtpPickerFragment() {
|
||||
super();
|
||||
ftp = new FTPClient();
|
||||
}
|
||||
|
||||
public static AbstractFilePickerFragment<FtpFile> newInstance(String startPath, int mode,
|
||||
boolean allowMultiple,
|
||||
boolean allowCreateDir,
|
||||
boolean allowExistingFile,
|
||||
boolean singleClick,
|
||||
String server, int port,
|
||||
String username,
|
||||
String password,
|
||||
String rootDir) {
|
||||
FtpPickerFragment fragment = new FtpPickerFragment();
|
||||
// Add arguments
|
||||
fragment.setArgs(startPath, mode, allowMultiple, allowCreateDir,
|
||||
allowExistingFile, singleClick);
|
||||
Bundle args = fragment.getArguments();
|
||||
|
||||
// Add ftp related stuff
|
||||
args.putString(KEY_FTP_ROOTDIR, rootDir);
|
||||
args.putString(KEY_FTP_SERVER, server);
|
||||
args.putInt(KEY_FTP_PORT, port);
|
||||
if (username != null && password != null) {
|
||||
args.putString(KEY_FTP_USERNAME, username);
|
||||
args.putString(KEY_FTP_PASSWORD, password);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle b) {
|
||||
super.onCreate(b);
|
||||
|
||||
Bundle args = getArguments();
|
||||
this.server = args.getString(KEY_FTP_SERVER);
|
||||
this.port = args.getInt(KEY_FTP_PORT);
|
||||
this.username = args.getString(KEY_FTP_USERNAME) != null ? args.getString(KEY_FTP_USERNAME) : "anonymous";
|
||||
this.password = args.getString(KEY_FTP_PASSWORD) != null ? args.getString(KEY_FTP_PASSWORD) : "anonymous";
|
||||
this.rootDir = args.getString(KEY_FTP_ROOTDIR) != null ? args.getString(KEY_FTP_ROOTDIR) : "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View inflateRootView(LayoutInflater inflater, ViewGroup container) {
|
||||
// Load the specific layout we created for dropbox/ftp
|
||||
View view = inflater.inflate(R.layout.fragment_loading_filepicker, container, false);
|
||||
// And bind the progress bar
|
||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the path is a directory and not a file.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDir(@NonNull FtpFile path) {
|
||||
return path.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return filename of path
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName(@NonNull FtpFile path) {
|
||||
return path.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the path to a URI for the return intent
|
||||
*
|
||||
* @return a Uri
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Uri toUri(@NonNull FtpFile path) {
|
||||
String user = "";
|
||||
if (!username.isEmpty()) {
|
||||
user = username;
|
||||
if (!password.isEmpty()) {
|
||||
user += ":" + password;
|
||||
}
|
||||
user += "@";
|
||||
}
|
||||
return Uri.parse("ftp://" + user + server + ":" + port + path.getPath());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the parent directory. Should return the root if
|
||||
* from is root.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public FtpFile getParent(@NonNull FtpFile from) {
|
||||
if (from.getPath().equals(getRoot().getPath())) {
|
||||
// Already at root, we can't go higher
|
||||
return from;
|
||||
} else if (from.getParentFile() != null) {
|
||||
return from.getParentFile();
|
||||
} else {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFullPath(@NonNull FtpFile path) {
|
||||
return path.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the path to the type used.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public FtpFile getPath(@NonNull String path) {
|
||||
return new FtpFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root path (lowest allowed).
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public FtpFile getRoot() {
|
||||
return new FtpDir(rootDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (ftp.isConnected()) {
|
||||
try {
|
||||
ftp.disconnect();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a loader that lists the files in the current path,
|
||||
* and monitors changes.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<SortedList<FtpFile>> getLoader() {
|
||||
return new AsyncTaskLoader<SortedList<FtpFile>>(getContext()) {
|
||||
@Override
|
||||
public SortedList<FtpFile> loadInBackground() {
|
||||
SortedList<FtpFile> sortedList = new SortedList<>(FtpFile.class, new SortedListAdapterCallback<FtpFile>(getDummyAdapter()) {
|
||||
@Override
|
||||
public int compare(FtpFile lhs, FtpFile rhs) {
|
||||
if (lhs.isDirectory() && !rhs.isDirectory()) {
|
||||
return -1;
|
||||
} else if (rhs.isDirectory() && !lhs.isDirectory()) {
|
||||
return 1;
|
||||
} else {
|
||||
return lhs.getName().compareToIgnoreCase(rhs.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(FtpFile oldItem, FtpFile newItem) {
|
||||
return oldItem.getName().equals(newItem.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(FtpFile item1, FtpFile item2) {
|
||||
return item1.getName().equals(item2.getName());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (!ftp.isConnected()) {
|
||||
// Connect
|
||||
try {
|
||||
ftp.connect(server, port);
|
||||
|
||||
ftp.setFileType(FTP.ASCII_FILE_TYPE);
|
||||
ftp.enterLocalPassiveMode();
|
||||
ftp.setUseEPSVwithIPv4(false);
|
||||
|
||||
if (!(loggedIn = ftp.login(username, password))) {
|
||||
ftp.logout();
|
||||
Log.e(TAG, "Login failed");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (ftp.isConnected()) {
|
||||
try {
|
||||
ftp.disconnect();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "Could not connect to server.");
|
||||
}
|
||||
}
|
||||
|
||||
if (loggedIn) {
|
||||
try {
|
||||
// handle if directory does not exist. Fall back to root.
|
||||
if (mCurrentPath == null || !mCurrentPath.isDirectory()) {
|
||||
mCurrentPath = getRoot();
|
||||
}
|
||||
|
||||
sortedList.beginBatchedUpdates();
|
||||
for (FTPFile f : ftp.listFiles(mCurrentPath.getPath())) {
|
||||
FtpFile file;
|
||||
if (f.isDirectory()) {
|
||||
file = new FtpDir(mCurrentPath, f.getName());
|
||||
} else {
|
||||
file = new FtpFile(mCurrentPath, f.getName());
|
||||
}
|
||||
if (isItemVisible(file)) {
|
||||
sortedList.add(file);
|
||||
}
|
||||
}
|
||||
sortedList.endBatchedUpdates();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return sortedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to start the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
super.onStartLoading();
|
||||
|
||||
// handle if directory does not exist. Fall back to root.
|
||||
if (mCurrentPath == null || !mCurrentPath.isDirectory()) {
|
||||
mCurrentPath = getRoot();
|
||||
}
|
||||
|
||||
forceLoad();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the list to determine whether a file should be displayed or not.
|
||||
* Default behavior is to always display folders. If files can be selected,
|
||||
* then files are also displayed. Override this method to enable other
|
||||
* filtering behaviour, like only displaying files with specific extensions (.zip, .txt, etc).
|
||||
*
|
||||
* @param file to maybe add. Can be either a directory or file.
|
||||
* @return True if item should be added to the list, false otherwise
|
||||
*/
|
||||
protected boolean isItemVisible(final FtpFile file) {
|
||||
return file.isDirectory() || (mode == MODE_FILE || mode == MODE_FILE_AND_DIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name is validated to be non-null, non-empty and not containing any
|
||||
* slashes.
|
||||
*
|
||||
* @param name The name of the folder the user wishes to create.
|
||||
*/
|
||||
@Override
|
||||
public void onNewFolder(@NonNull String name) {
|
||||
AsyncTask<String, Void, FtpFile> task = new AsyncTask<String, Void, FtpFile>() {
|
||||
|
||||
@Override
|
||||
protected FtpFile doInBackground(String... names) {
|
||||
FtpFile result = null;
|
||||
if (names.length > 0) {
|
||||
result = onNewFolderAsync(names[0]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(FtpFile folder) {
|
||||
if (folder != null) {
|
||||
refresh(folder);
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.nnf_create_folder_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
task.execute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are loading, then hide the list and show the progress bar instead.
|
||||
*
|
||||
* @param nextPath path to list files for
|
||||
*/
|
||||
@Override
|
||||
protected void refresh(@NonNull FtpFile nextPath) {
|
||||
super.refresh(nextPath);
|
||||
if (isLoading) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<SortedList<FtpFile>> loader, SortedList<FtpFile> data) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
super.onLoadFinished(loader, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<SortedList<FtpFile>> loader) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
super.onLoaderReset(loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The name of the folder the user wishes to create.
|
||||
*/
|
||||
public FtpFile onNewFolderAsync(String name) {
|
||||
FtpDir folder = new FtpDir(mCurrentPath, name);
|
||||
try {
|
||||
if (ftp.makeDirectory(folder.getPath())) {
|
||||
// Success, return result
|
||||
return folder;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IO Exception: " + folder.getPath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.multimedia;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* All this class does is return a suitable fragment.
|
||||
*/
|
||||
public class MultimediaPickerActivity extends AbstractFilePickerActivity {
|
||||
|
||||
public MultimediaPickerActivity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(
|
||||
@Nullable final String startPath, final int mode, final boolean allowMultiple,
|
||||
final boolean allowCreateDir, final boolean allowExistingFile,
|
||||
final boolean singleClick) {
|
||||
AbstractFilePickerFragment<File> fragment = new MultimediaPickerFragment();
|
||||
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
|
||||
fragment.setArgs(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
||||
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.multimedia;
|
||||
|
||||
/**
|
||||
* Duplicate to allow second theme to be used.
|
||||
*/
|
||||
public class MultimediaPickerActivity2 extends MultimediaPickerActivity {
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.nononsenseapps.filepicker.sample.multimedia;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.nononsenseapps.filepicker.FilePickerFragment;
|
||||
import com.nononsenseapps.filepicker.sample.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A sample which demonstrates how appropriate methods
|
||||
* can be overwritten in order to enable enhanced
|
||||
* capabilities, in this case showing thumbnails of images.
|
||||
* <p/>
|
||||
* I am still listing all files, so I extend from the ready made
|
||||
* SD-card browser classes. This allows this class to focus
|
||||
* entirely on the image side of things.
|
||||
* <p/>
|
||||
* To load the image I am using the super great Glide library
|
||||
* which only requires a single line of code in this file.
|
||||
*/
|
||||
public class MultimediaPickerFragment extends FilePickerFragment {
|
||||
|
||||
// Make sure these do not collide with LogicHandler.VIEWTYPE codes.
|
||||
// They are 1-2, so 11 leaves a lot of free space in between.
|
||||
private static final int VIEWTYPE_IMAGE_CHECKABLE = 11;
|
||||
private static final int VIEWTYPE_IMAGE = 12;
|
||||
|
||||
private static final String[] MULTIMEDIA_EXTENSIONS =
|
||||
new String[]{".png", ".jpg", ".gif", ".mp4"};
|
||||
|
||||
/**
|
||||
* An extremely simple method for identifying multimedia. This
|
||||
* could be improved, but it's good enough for this example.
|
||||
*
|
||||
* @param file which could be an image or a video
|
||||
* @return true if the file can be previewed, false otherwise
|
||||
*/
|
||||
protected boolean isMultimedia(File file) {
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (isDir(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String path = file.getPath().toLowerCase();
|
||||
for (String ext : MULTIMEDIA_EXTENSIONS) {
|
||||
if (path.endsWith(ext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we check if the file is an image, and if thus if we should create views corresponding
|
||||
* to our image layouts.
|
||||
*
|
||||
* @param position 0 - n, where the header has been subtracted
|
||||
* @param file to check type of
|
||||
* @return the viewtype of the item
|
||||
*/
|
||||
@Override
|
||||
public int getItemViewType(int position, @NonNull File file) {
|
||||
if (isMultimedia(file)) {
|
||||
if (isCheckable(file)) {
|
||||
return VIEWTYPE_IMAGE_CHECKABLE;
|
||||
} else {
|
||||
return VIEWTYPE_IMAGE;
|
||||
}
|
||||
} else {
|
||||
return super.getItemViewType(position, file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We override this method and provide some special views for images.
|
||||
* This is necessary to work around a bug on older Android versions (4.0.3 for example)
|
||||
* where setting a "tint" would just make the entire image a square of solid color.
|
||||
* <p/>
|
||||
* So the special layouts used here are merely "untinted" copies from the library.
|
||||
*
|
||||
* @param parent Containing view
|
||||
* @param viewType which the ViewHolder will contain
|
||||
* @return a DirViewHolder (or subclass thereof)
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case VIEWTYPE_IMAGE_CHECKABLE:
|
||||
return new CheckableViewHolder(LayoutInflater.from(getActivity())
|
||||
.inflate(R.layout.listitem_image_checkable, parent, false));
|
||||
case VIEWTYPE_IMAGE:
|
||||
return new DirViewHolder(LayoutInflater.from(getActivity())
|
||||
.inflate(R.layout.listitem_image, parent, false));
|
||||
default:
|
||||
return super.onCreateViewHolder(parent, viewType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding this method allows us to inject a preview image
|
||||
* in the layout
|
||||
*
|
||||
* @param vh to bind data from either a file or directory
|
||||
* @param position 0 - n, where the header has been subtracted
|
||||
* @param file to show info about
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull DirViewHolder vh, int position, @NonNull File file) {
|
||||
// Let the super method do its thing with checkboxes and text
|
||||
super.onBindViewHolder(vh, position, file);
|
||||
|
||||
// Here we load the preview image if it is an image file
|
||||
final int viewType = getItemViewType(position, file);
|
||||
if (viewType == VIEWTYPE_IMAGE_CHECKABLE || viewType == VIEWTYPE_IMAGE) {
|
||||
// Need to set it to visible because the base code will set it to invisible by default
|
||||
vh.icon.setVisibility(View.VISIBLE);
|
||||
// Just load the image
|
||||
Glide.with(this).load(file).into((ImageView) vh.icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.root;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
/**
|
||||
* A dialog which tells the user that no SU binary is available
|
||||
*/
|
||||
public class SUErrorFragment extends DialogFragment {
|
||||
|
||||
private static final String TAG = "SUErrorFragment";
|
||||
|
||||
public static void showDialog(@NonNull final FragmentManager fm) {
|
||||
SUErrorFragment d = new SUErrorFragment();
|
||||
d.show(fm, TAG);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage("No read permisson, root unavailable")
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.root;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SUPickerActivity extends AbstractFilePickerActivity<File> {
|
||||
|
||||
public SUPickerActivity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractFilePickerFragment<File> getFragment(@Nullable String startPath,
|
||||
int mode,
|
||||
boolean allowMultiple,
|
||||
boolean allowCreateDir,
|
||||
boolean allowExistingFile,
|
||||
boolean singleClick) {
|
||||
AbstractFilePickerFragment<File> fragment = new SUPickerFragment();
|
||||
// startPath is allowed to be null. In that case, default folder should be SD-card and
|
||||
// not "/"
|
||||
fragment.setArgs(
|
||||
startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
|
||||
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.root;
|
||||
|
||||
/**
|
||||
* Just for second theme
|
||||
*/
|
||||
public class SUPickerActivity2 extends SUPickerActivity {
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package com.nononsenseapps.filepicker.sample.root;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nononsenseapps.filepicker.FilePickerFragment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import eu.chainfire.libsuperuser.Shell;
|
||||
|
||||
/**
|
||||
* An example picker which calls out to LibSU to get Root-permissions to view otherwise hidden files.
|
||||
*/
|
||||
public class SUPickerFragment extends FilePickerFragment {
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(@NonNull File path) {
|
||||
// Return the combination of normal file permissions and SU permissions
|
||||
return super.hasPermission(path) & (!needSUPermission(path) | hasSUPermission());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handlePermission(@NonNull File path) {
|
||||
// Only call super if we don't have normal file permissions
|
||||
if (!super.hasPermission(path)) {
|
||||
super.handlePermission(path);
|
||||
}
|
||||
// Only if we need SU permissions
|
||||
if (needSUPermission(path) && !hasSUPermission()) {
|
||||
handleSUPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean haveReadPermission(@NonNull File file) {
|
||||
List<String> result =
|
||||
Shell.SH.run("test -r " + file.getAbsolutePath() + " && echo \"rootsuccess\"");
|
||||
return result != null && !result.isEmpty() && "rootsuccess".equals(result.get(0));
|
||||
}
|
||||
|
||||
private boolean needSUPermission(@NonNull File path) {
|
||||
return !haveReadPermission(path);
|
||||
}
|
||||
|
||||
private boolean isSUAvailable() {
|
||||
return Shell.SU.available();
|
||||
}
|
||||
|
||||
private boolean hasSUPermission() {
|
||||
if (isSUAvailable()) {
|
||||
List<String> result = Shell.SU.run("ls -l /");
|
||||
if (result != null && !result.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleSUPermission() {
|
||||
if (isSUAvailable()) {
|
||||
// request
|
||||
String suVersion = Shell.SU.version(false);
|
||||
String suVersionInternal = Shell.SU.version(true);
|
||||
Log.d("libsuperuser: ", "suVersion:"+suVersion+" suVersionInternal:"+suVersionInternal);
|
||||
} else {
|
||||
// Notify that no root access available
|
||||
SUErrorFragment.showDialog(getFragmentManager());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
|
@ -1,215 +0,0 @@
|
|||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.nononsenseapps.filepicker.sample.NoNonsenseFilePicker">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/radioGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioFile"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:checked="true"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioNewFile"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select new file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioDir"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select directory" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioFilesAndDirs"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Select any" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkAllowMultiple"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Multiple items" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkSingleClick"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Single selection returns immediately" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkAllowCreateDir"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Allow creation of directories" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkAllowExistingFile"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:checked="true"
|
||||
android:text="Allow selection of existing (new) file" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkLightTheme"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="left"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Use light theme"
|
||||
android:checked="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_sd"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick SD-card" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_image"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick SD-card with multimedia preview" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_ftp"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick FTP" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_dropbox"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick Dropbox" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_fastscroll"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick With Fast Scroller" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_root"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="light"
|
||||
android:gravity="center"
|
||||
android:text="Pick With Root Access" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
style="?android:textAppearanceMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="light"
|
||||
android:padding="16dp"
|
||||
android:text="Result will be displayed here" />
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/nnf_picker_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?nnf_toolbarTheme">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nnf_current_dir"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/nnf_buttons_container"
|
||||
android:layout_below="@+id/nnf_picker_toolbar"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
app:fastScrollAutoHide="true"
|
||||
app:fastScrollAutoHideDelay="1500"
|
||||
app:fastScrollPopupBgColor="?colorAccent"
|
||||
app:fastScrollPopupTextColor="@android:color/primary_text_dark"
|
||||
app:fastScrollThumbColor="?colorAccent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/nnf_buttons_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_cancel"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_ok"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/ok"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_newfile_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/nnf_text_filename"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/nnf_filename"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/nnf_button_ok_newfile"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:hint="@android:string/ok"
|
||||
android:src="@drawable/nnf_ic_save_black_24dp"
|
||||
android:tint="?attr/nnf_save_icon_color"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_above="@id/nnf_buttons_container"
|
||||
android:background="?nnf_separator_color"/>
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<!-- This file is just a copy of the normal nnf_fragment_filepicker, with an added progress bar -->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.nononsenseapps.filepicker.FilePickerActivity">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/nnf_picker_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?nnf_toolbarTheme">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nnf_current_dir"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="start"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/nnf_buttons_container"
|
||||
android:layout_below="@+id/nnf_picker_toolbar"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
tools:listitem="@layout/nnf_filepicker_listitem_dir"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/nnf_buttons_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_cancel"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/nnf_button_ok"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/ok"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/nnf_newfile_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/nnf_text_filename"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/nnf_filename"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/nnf_button_ok_newfile"
|
||||
style="?attr/borderlessButtonStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:hint="@android:string/ok"
|
||||
android:src="@drawable/nnf_ic_save_black_24dp"
|
||||
android:tint="?attr/nnf_save_icon_color"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_above="@id/nnf_button_container"
|
||||
android:background="?nnf_separator_color"/>
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nnf_item_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeight"
|
||||
android:background="?selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:minHeight="?android:listPreferredItemHeight"
|
||||
android:nextFocusLeft="@+id/nnf_button_cancel"
|
||||
android:nextFocusRight="@+id/nnf_button_ok"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!--suppress AndroidDomInspection -->
|
||||
<ImageView
|
||||
android:id="@+id/item_icon"
|
||||
android:layout_width="?android:listPreferredItemHeight"
|
||||
android:layout_height="?android:listPreferredItemHeight"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="visible"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
style="?android:textAppearanceLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:padding="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/nnf_name" />
|
||||
</LinearLayout>
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nnf_item_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeight"
|
||||
android:background="?selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:minHeight="?android:listPreferredItemHeight"
|
||||
android:nextFocusLeft="@+id/nnf_button_cancel"
|
||||
android:nextFocusRight="@id/checkbox"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
<!--suppress AndroidDomInspection -->
|
||||
<ImageView
|
||||
android:id="@+id/item_icon"
|
||||
android:layout_width="?android:listPreferredItemHeight"
|
||||
android:layout_height="?android:listPreferredItemHeight"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="visible"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
style="?android:textAppearanceLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:padding="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/nnf_name" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:nextFocusLeft="@id/nnf_item_container"
|
||||
android:nextFocusRight="@+id/nnf_button_ok"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context="com.nononsenseapps.filepicker.sample.NoNonsenseFilePicker">
|
||||
|
||||
<item android:id="@+id/action_settings"
|
||||
android:title="@string/action_settings"
|
||||
android:orderInCategory="100"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<color name="primary">#607D8B</color>
|
||||
<color name="primary_dark">#455A64</color>
|
||||
<color name="accent">#536DFE</color>
|
||||
|
||||
|
||||
<color name="primary2">#F44336</color>
|
||||
<color name="primary_dark2">#D32F2F</color>
|
||||
<color name="accent2">#FFAB00</color>
|
||||
</resources>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<string name="app_name">@string/title_activity_no_nonsense_file_picker</string>
|
||||
<string name="title_activity_no_nonsense_file_picker">
|
||||
NoNonsenseFilePicker Sample</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<!--
|
||||
~ This Source Code Form is subject to the terms of the Mozilla Public
|
||||
~ License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="SampleTheme" parent="NNF_BaseTheme">
|
||||
<item name="colorPrimary">@color/primary2</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark2</item>
|
||||
<item name="colorAccent">@color/accent2</item>
|
||||
|
||||
<item name="alertDialogTheme">@style/SampleAlertDialogTheme</item>
|
||||
</style>
|
||||
|
||||
<style name="SampleThemeLight" parent="NNF_BaseTheme.Light">
|
||||
<item name="nnf_list_item_divider">?android:attr/listDivider</item>
|
||||
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
|
||||
<item name="alertDialogTheme">@style/SampleAlertDialogThemeLight</item>
|
||||
|
||||
<!-- Make sure toolbar title is white -->
|
||||
<item name="nnf_toolbarTheme">@style/SampleToolbarTheme</item>
|
||||
</style>
|
||||
|
||||
<style name="SampleAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
|
||||
<item name="colorPrimary">@color/primary2</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark2</item>
|
||||
<item name="colorAccent">@color/accent2</item>
|
||||
</style>
|
||||
|
||||
<style name="SampleAlertDialogThemeLight" parent="Theme.AppCompat.Light.Dialog.Alert">
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
</style>
|
||||
|
||||
<style name="SampleToolbarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||
<item name="android:textColorPrimary">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
include ':sample', ':library', ':examples'
|
||||
include ':library'
|
||||
|
|
|
|||