merge branch 'feature/purge'

This commit is contained in:
Arhipov 2017-04-06 18:56:41 +03:00
commit cc5d9fe06a
77 changed files with 18 additions and 4501 deletions

View File

@ -10,8 +10,6 @@ buildscript {
repositories {
jcenter()
}
dependencies {
}
}
allprojects {

1
examples/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

@ -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'
}

View File

@ -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 *;
#}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -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>

View File

@ -1,3 +0,0 @@
<resources>
<string name="app_name">Examples</string>
</resources>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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'
}

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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.

View File

@ -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'
}

View File

@ -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)

View File

@ -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.

Binary file not shown.

Binary file not shown.

View File

@ -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 *;
#}

View File

@ -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")));
}
}

View File

@ -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")));
}
}

View File

@ -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()");
}
}
}

View File

@ -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")));
}
}

View File

@ -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")));
}
}

View File

@ -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>

View File

@ -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 {
}

View File

@ -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());
}
}
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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();
}
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -1,7 +0,0 @@
package com.nononsenseapps.filepicker.sample.fastscroller;
/**
* Just for theme sample purposes
*/
public class FastScrollerFilePickerActivity2 extends FastScrollerFilePickerActivity {
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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, "/");
}
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -1,7 +0,0 @@
package com.nononsenseapps.filepicker.sample.root;
/**
* Just for second theme
*/
public class SUPickerActivity2 extends SUPickerActivity {
}

View File

@ -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());
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -4,4 +4,4 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
include ':sample', ':library', ':examples'
include ':library'