{
12 | public FileHolderHasName(Matcher super String> hasFileHolderMatcher) {
13 | super(hasFileHolderMatcher, "with getName()", "getName()");
14 | }
15 |
16 | @Override
17 | protected String featureValueOf(T actual) {
18 | if (actual instanceof FileHolder) {
19 | return ((FileHolder) actual).getName();
20 | } else {
21 | return null;
22 | }
23 | }
24 |
25 | /**
26 | * Creates a matcher that matches any examined object whose toString method
27 | * returns a value equalTo the specified string.
28 | *
29 | * For example:
30 | * assertThat(true, hasToString("TRUE"))
31 | *
32 | * @param expectedName the expected toString result
33 | */
34 | @Factory
35 | public static Matcher hasName(String expectedName) {
36 | return new FileHolderHasName(equalTo(expectedName));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CreateDirectoryArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
22 |
23 | import java.io.File;
24 |
25 | public class CreateDirectoryArguments extends FileOperation.Arguments {
26 | private CreateDirectoryArguments(@NonNull File newDirectory) {
27 | super(newDirectory);
28 | }
29 |
30 | public static CreateDirectoryArguments createDirectoryArguments(@NonNull File newDirectory) {
31 | return new CreateDirectoryArguments(newDirectory);
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_text_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_two.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stat_notify_paste.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logo.xml:
--------------------------------------------------------------------------------
1 |
16 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stat_notify_compress.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_stat_notify_extract.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/actor/User.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.test.actor;
2 |
3 | import android.app.Activity;
4 | import android.support.test.rule.ActivityTestRule;
5 |
6 | import com.veniosg.dir.test.actor.action.LaunchesActions;
7 | import com.veniosg.dir.test.actor.action.SelectsActions;
8 | import com.veniosg.dir.test.actor.action.TypesActions;
9 | import com.veniosg.dir.test.actor.assertion.CannotSeeAssertions;
10 | import com.veniosg.dir.test.actor.assertion.SeesAssertions;
11 |
12 | public class User {
13 | private final SeesAssertions sees = new SeesAssertions();
14 | private final CannotSeeAssertions cannotSeeAssertions = new CannotSeeAssertions();
15 | private final SelectsActions selects = new SelectsActions();
16 | private final TypesActions types = new TypesActions();
17 | private final LaunchesActions launches;
18 |
19 | public User(ActivityTestRule extends Activity> activityRule) {
20 | launches = new LaunchesActions(activityRule);
21 | }
22 |
23 | public SeesAssertions sees() {
24 | return sees;
25 | }
26 |
27 | public SelectsActions selects() {
28 | return selects;
29 | }
30 |
31 | public CannotSeeAssertions cannotSee() {
32 | return cannotSeeAssertions;
33 | }
34 |
35 | public TypesActions types() {
36 | return types;
37 | }
38 |
39 | public LaunchesActions launches() {
40 | return launches;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/StorageAccessManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.access;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import java.io.File;
22 |
23 | public interface StorageAccessManager {
24 | boolean hasWriteAccess(@NonNull File fileInStorage);
25 | void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener);
26 | boolean isSafBased();
27 |
28 | interface AccessPermissionListener {
29 | void granted();
30 | void denied();
31 |
32 | /**
33 | * Called when the grant succeeded but the file is still not writable.
34 | * This can mean that the grant happened on the wrong directory.
35 | */
36 | void error();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/ui/toast/ToastDisplayer.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.android.ui.toast;
2 |
3 | import android.content.Context;
4 |
5 | import com.veniosg.dir.R;
6 |
7 | import static android.widget.Toast.LENGTH_LONG;
8 | import static android.widget.Toast.LENGTH_SHORT;
9 | import static android.widget.Toast.makeText;
10 |
11 | public class ToastDisplayer {
12 | private Context context;
13 |
14 | public ToastDisplayer(Context context) {
15 | this.context = context;
16 | }
17 |
18 | public void renameSuccess() {
19 | makeText(context, R.string.rename_success, LENGTH_SHORT).show();
20 | }
21 |
22 | public void renameFailure() {
23 | makeText(context, R.string.rename_failure, LENGTH_SHORT).show();
24 | }
25 |
26 | public void createDirectorySuccess() {
27 | makeText(context, R.string.create_dir_success, LENGTH_SHORT).show();
28 | }
29 |
30 | public void createDirectoryFailure() {
31 | makeText(context, R.string.create_dir_failure, LENGTH_SHORT).show();
32 | }
33 |
34 | public void deleteSuccess() {
35 | makeText(context, R.string.delete_success, LENGTH_SHORT).show();
36 | }
37 |
38 | public void deleteFailure() {
39 | makeText(context, R.string.delete_failure, LENGTH_SHORT).show();
40 | }
41 |
42 | public void grantAccessWrongDirectory() {
43 | makeText(context, R.string.select_sd_root, LENGTH_LONG).show();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/fdroid/java/com.veniosg.dir.mvvm.model.iab/FDroidBillingManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.veniosg.dir.mvvm.model.iab;
17 |
18 | import android.app.Activity;
19 | import android.content.Context;
20 |
21 | public class FDroidBillingManager implements BillingManager {
22 | @Override
23 | public void init(Context context, OnPurchasedListener onPurchasedListener,
24 | OnBillingUnavailableListener onUnavailableListener) {
25 | onUnavailableListener.onBillingUnavailable();
26 | }
27 |
28 | @Override
29 | public boolean hasPurchasedDonation() {
30 | return false;
31 | }
32 |
33 | @Override
34 | public void purchaseDonation(Activity activity) {
35 | }
36 |
37 | @Override
38 | public void consumePurchase(Purchase p, OnConsumedListener onConsumedListener) {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/iab/BillingManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.veniosg.dir.mvvm.model.iab;
17 |
18 | import android.app.Activity;
19 | import android.content.Context;
20 |
21 | public interface BillingManager {
22 | void init(Context context,
23 | OnPurchasedListener onPurchasedListener,
24 | OnBillingUnavailableListener onUnavailableListener);
25 | boolean hasPurchasedDonation();
26 | void purchaseDonation(Activity activity);
27 | void consumePurchase(Purchase p, OnConsumedListener onConsumedListener);
28 |
29 | interface OnPurchasedListener {
30 | void onPurchased(Purchase p);
31 | }
32 |
33 | interface OnConsumedListener {
34 | void onConsumed();
35 | }
36 |
37 | interface OnBillingUnavailableListener {
38 | void onBillingUnavailable();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_five.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 | 4dp
18 |
19 | 72dp
20 | 88dp
21 |
22 | 16dp
23 | 72dp
24 |
25 | 24dp
26 | 80dp
27 |
28 | 16dp
29 | 24dp
30 |
31 |
32 |
33 |
34 | 48dp
35 | 48dp
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pref_palette.xml:
--------------------------------------------------------------------------------
1 |
16 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/RenameArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
22 |
23 | import java.io.File;
24 |
25 | public class RenameArguments extends FileOperation.Arguments {
26 | @NonNull
27 | private final File fileToRename;
28 |
29 | private RenameArguments(@NonNull File fileToRename, @NonNull String newName) {
30 | super(new File(fileToRename.getParent(), newName));
31 | this.fileToRename = fileToRename;
32 | }
33 |
34 | public static RenameArguments renameArguments(@NonNull File fileToRename, @NonNull String newName) {
35 | return new RenameArguments(fileToRename, newName);
36 | }
37 |
38 | @NonNull
39 | public File getFileToRename() {
40 | return fileToRename;
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/ui/OperationStatusDisplayerInjector.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.mvvm.model.storage.operation.ui;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.File;
6 |
7 | public abstract class OperationStatusDisplayerInjector {
8 | private static final OperationStatusDisplayer NO_OP_DISPLAYER = new OperationStatusDisplayer() {
9 | @Override
10 | public void initChannels() {
11 | }
12 |
13 | @Override
14 | public void showCopySuccess(int operationId, File destDir) {
15 | }
16 |
17 | @Override
18 | public void showCopyFailure(int operationId, File destDir) {
19 | }
20 |
21 | @Override
22 | public void showMoveProgress(int operationId, File destDir, File moving, int progress, int max) {
23 | }
24 |
25 | @Override
26 | public void showMoveSuccess(int operationId, File destDir) {
27 | }
28 |
29 | @Override
30 | public void showMoveFailure(int operationId, File destDir) {
31 | }
32 |
33 | @Override
34 | public void showCopyProgress(int operationId, File destDir, File copying, int progress, int max) {
35 | }
36 | };
37 |
38 | private OperationStatusDisplayerInjector() {
39 | }
40 |
41 | public static OperationStatusDisplayer operationStatusDisplayer(Context context) {
42 | return new NotificationOperationStatusDisplayer(context);
43 | }
44 |
45 | public static OperationStatusDisplayer noOpStatusDisplayer() {
46 | return NO_OP_DISPLAYER;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/DeleteArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.FileHolder;
22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
23 |
24 | import java.io.File;
25 |
26 | public class DeleteArguments extends FileOperation.Arguments {
27 | @NonNull
28 | private final FileHolder[] victims;
29 |
30 | private DeleteArguments(@NonNull File parentDirectory, @NonNull FileHolder... victims) {
31 | super(parentDirectory);
32 | this.victims = victims;
33 | }
34 |
35 | public static DeleteArguments deleteArgs(@NonNull File parentDirectory, @NonNull FileHolder... victims) {
36 | return new DeleteArguments(parentDirectory, victims);
37 | }
38 |
39 | @NonNull
40 | public FileHolder[] getVictims() {
41 | return victims;
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/ExtractArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.FileHolder;
22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
23 |
24 | import java.io.File;
25 | import java.util.List;
26 |
27 | public class ExtractArguments extends FileOperation.Arguments {
28 | @NonNull
29 | private final List zipFiles;
30 |
31 | private ExtractArguments(@NonNull File target, @NonNull List zipFiles) {
32 | super(target);
33 | this.zipFiles = zipFiles;
34 | }
35 |
36 | public static ExtractArguments extractArgs(@NonNull File target, @NonNull List zipFiles) {
37 | return new ExtractArguments(target, zipFiles);
38 | }
39 |
40 | @NonNull
41 | public List getZipFiles() {
42 | return zipFiles;
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/ui/AnimatorSynchroniser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.ui;
18 |
19 | import android.animation.Animator;
20 | import android.animation.AnimatorSet;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class AnimatorSynchroniser {
26 | private static final int DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE = 2;
27 |
28 | private List mAnimators = new ArrayList<>(DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE);
29 |
30 | /**
31 | * Add an animation and fire it as soon as the number of waiting animators reaches the max.
32 | */
33 | public synchronized void addWaitingAnimation(Animator anim) {
34 | mAnimators.add(anim);
35 |
36 | if (mAnimators.size() >= DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE) {
37 | AnimatorSet animSet = new AnimatorSet();
38 | animSet.playTogether(mAnimators);
39 | animSet.start();
40 |
41 | mAnimators.clear();
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CompressArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.FileHolder;
22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
23 |
24 | import java.io.File;
25 | import java.util.List;
26 |
27 | public class CompressArguments extends FileOperation.Arguments {
28 | @NonNull
29 | private final List toCompress;
30 |
31 | private CompressArguments(@NonNull File target, @NonNull List toCompress) {
32 | super(target);
33 | this.toCompress = toCompress;
34 | }
35 |
36 | public static CompressArguments compressArgs(@NonNull File target, @NonNull List toCompress) {
37 | return new CompressArguments(target, toCompress);
38 | }
39 |
40 | @NonNull
41 | public List getToCompress() {
42 | return toCompress;
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/actor/assertion/LaunchedAssertions.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.test.actor.assertion;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | import com.veniosg.dir.android.activity.SearchActivity;
8 | import com.veniosg.dir.android.util.FileUtils;
9 | import com.veniosg.dir.mvvm.model.FileHolder;
10 |
11 | import java.io.File;
12 |
13 | import static android.content.Intent.ACTION_SEARCH;
14 | import static android.support.test.espresso.intent.Intents.intended;
15 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
16 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
17 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData;
18 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
19 | import static org.hamcrest.Matchers.any;
20 | import static org.hamcrest.core.AllOf.allOf;
21 |
22 | public class LaunchedAssertions {
23 | public void viewFileIntent(File file) {
24 | intended(allOf(
25 | hasAction(Intent.ACTION_VIEW),
26 | hasData(FileUtils.getUri(new FileHolder(file, null, null))),
27 | hasType(any(String.class))
28 | ));
29 | }
30 |
31 | public void searchIntentFor(File file) {
32 | intended(allOf(
33 | hasAction(ACTION_SEARCH),
34 | hasComponent(new ComponentName("com.veniosg.dir", SearchActivity.class.getName())),
35 | hasData(new Uri.Builder().path(file.getAbsolutePath()).build())
36 | ));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/ExternalStorageAccessManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.access;
18 |
19 | import android.content.Context;
20 | import android.support.annotation.NonNull;
21 |
22 | import java.io.File;
23 |
24 | public class ExternalStorageAccessManager implements StorageAccessManager {
25 | private final StorageAccessManager delegate;
26 |
27 | public ExternalStorageAccessManager(Context context) {
28 | delegate = new SafStorageAccessManager(context);
29 | }
30 |
31 | @Override
32 | public boolean hasWriteAccess(@NonNull File fileInStorage) {
33 | return delegate.hasWriteAccess(fileInStorage);
34 | }
35 |
36 | @Override
37 | public void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener) {
38 | delegate.requestWriteAccess(fileInStorage, listener);
39 | }
40 |
41 | @Override
42 | public boolean isSafBased() {
43 | return delegate.isSafBased();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/MoveArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.FileHolder;
22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
23 |
24 | import java.io.File;
25 | import java.util.List;
26 |
27 | import static java.util.Collections.unmodifiableList;
28 |
29 | public class MoveArguments extends FileOperation.Arguments {
30 | @NonNull
31 | private final List filesToMove;
32 |
33 | private MoveArguments(@NonNull List toMove, @NonNull File to) {
34 | super(to);
35 | this.filesToMove = toMove;
36 | }
37 |
38 | public static MoveArguments moveArgs(List toMove, File to) {
39 | return new MoveArguments(toMove, to);
40 | }
41 |
42 | @NonNull
43 | public List getFilesToMove() {
44 | return unmodifiableList(filesToMove);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CopyArguments.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation.argument;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import com.veniosg.dir.mvvm.model.FileHolder;
22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation;
23 |
24 | import java.io.File;
25 | import java.util.List;
26 |
27 | import static java.util.Collections.unmodifiableList;
28 |
29 | public class CopyArguments extends FileOperation.Arguments {
30 | @NonNull
31 | private final List filesToCopy;
32 |
33 | private CopyArguments(@NonNull List toCopy, @NonNull File to) {
34 | super(to);
35 | this.filesToCopy = toCopy;
36 | }
37 |
38 | public static CopyArguments copyArgs(@NonNull List toCopy, @NonNull File to) {
39 | return new CopyArguments(toCopy, to);
40 | }
41 |
42 | @NonNull
43 | public List getFilesToCopy() {
44 | return unmodifiableList(filesToCopy);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings_not_for_translation.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 | Dir
19 |
20 |
21 | - 1
22 | - 2
23 | - 3
24 |
25 |
26 | - 0
27 | - 1
28 | - 2
29 |
30 |
31 | - @string/preference_sortby_name
32 | - @string/preference_sortby_size
33 | - @string/preference_sortby_last_modified
34 |
35 |
36 | - @string/app_name
37 | - @string/preference_theme_grayscale
38 | - @string/preference_theme_dark
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_filemanager.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
22 |
26 |
27 |
28 |
29 |
33 |
34 |
35 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/dialog/OverwriteFileDialog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.dialog;
18 |
19 | import android.app.AlertDialog;
20 | import android.app.Dialog;
21 | import android.content.DialogInterface;
22 | import android.os.Bundle;
23 | import android.support.annotation.NonNull;
24 | import android.support.v4.app.DialogFragment;
25 |
26 | import com.veniosg.dir.R;
27 |
28 | public class OverwriteFileDialog extends DialogFragment {
29 | @NonNull
30 | @Override
31 | public Dialog onCreateDialog(Bundle savedInstanceState) {
32 | return new AlertDialog.Builder(getActivity())
33 | .setTitle(R.string.file_exists)
34 | .setMessage(R.string.overwrite_question)
35 | .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
36 | @Override
37 | public void onClick(DialogInterface dialog, int which) {
38 | ((Overwritable) getTargetFragment()).overwrite();
39 | }
40 | })
41 | .setNegativeButton(R.string.no, null)
42 | .create();
43 | }
44 |
45 | public interface Overwritable {
46 | void overwrite();
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/SdaStorageAccessManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.access;
18 |
19 | import android.content.Context;
20 | import android.support.annotation.NonNull;
21 |
22 | import java.io.File;
23 |
24 | /**
25 | * Uses the Scoped Directory Access to handle external storage permissions.
26 | * This provides a much improved UX and interface compared to the Storage Access Framework.
27 | */
28 | class SdaStorageAccessManager implements StorageAccessManager {
29 | // To be implemented as part of #77
30 | private final Context context;
31 |
32 | SdaStorageAccessManager(Context context) {
33 | this.context = context.getApplicationContext();
34 | }
35 |
36 | @Override
37 | public boolean hasWriteAccess(@NonNull File fileInStorage) {
38 | return false;
39 | }
40 |
41 | @Override
42 | public void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener) {
43 |
44 | }
45 |
46 | @Override
47 | public boolean isSafBased() {
48 | return false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_six.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_filelist_simple.xml:
--------------------------------------------------------------------------------
1 |
16 |
21 |
22 |
29 |
30 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_nine.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_navbar_edit.xml:
--------------------------------------------------------------------------------
1 |
16 |
23 |
27 |
31 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/dialog/DeleteAsyncTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.dialog;
18 |
19 | import android.content.Context;
20 | import android.os.AsyncTask;
21 | import android.support.annotation.NonNull;
22 |
23 | import com.veniosg.dir.mvvm.model.FileHolder;
24 | import com.veniosg.dir.mvvm.model.storage.operation.DeleteOperation;
25 |
26 | import static com.veniosg.dir.mvvm.model.storage.operation.FileOperationRunnerInjector.operationRunner;
27 | import static com.veniosg.dir.mvvm.model.storage.operation.argument.DeleteArguments.deleteArgs;
28 |
29 | class DeleteAsyncTask extends AsyncTask {
30 | private final Context context;
31 |
32 | DeleteAsyncTask(@NonNull Context context) {
33 | this.context = context;
34 | }
35 |
36 | @Override
37 | protected void onPreExecute() {
38 | }
39 |
40 | @Override
41 | protected Void doInBackground(FileHolder... params) {
42 | operationRunner(context).run(new DeleteOperation(context), deleteArgs(params[0].getFile().getParentFile(), params));
43 | return null;
44 | }
45 |
46 | @Override
47 | protected void onPostExecute(Void ignored) {
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_three.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sidenav_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/activity/PreferenceActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 OpenIntents.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.activity;
18 |
19 | import android.os.Bundle;
20 | import android.view.MenuItem;
21 |
22 | import com.veniosg.dir.R;
23 | import com.veniosg.dir.android.fragment.PreferenceFragment;
24 | import com.veniosg.dir.android.util.Utils;
25 |
26 | public class PreferenceActivity extends BaseActivity {
27 | private PreferenceFragment mFragment;
28 |
29 | @SuppressWarnings("ConstantConditions")
30 | @Override
31 | protected void onCreate(Bundle icicle) {
32 | super.onCreate(icicle);
33 |
34 | setContentView(R.layout.activity_generic);
35 | setupToolbar();
36 |
37 | mFragment = (PreferenceFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
38 | if(mFragment == null){
39 | mFragment = new PreferenceFragment();
40 | getFragmentManager()
41 | .beginTransaction()
42 | .add(R.id.fragment, mFragment, FRAGMENT_TAG)
43 | .commit();
44 | }
45 | }
46 |
47 | @Override
48 | public boolean onOptionsItemSelected(MenuItem item) {
49 | switch (item.getItemId()) {
50 | case android.R.id.home:
51 | Utils.showHome(this);
52 | return true;
53 | default:
54 | return super.onOptionsItemSelected(item);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/actor/action/TypesActions.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.test.actor.action;
2 |
3 | import android.widget.EditText;
4 |
5 | import com.veniosg.dir.R;
6 |
7 | import static android.support.test.espresso.Espresso.onView;
8 | import static android.support.test.espresso.action.ViewActions.clearText;
9 | import static android.support.test.espresso.action.ViewActions.pressImeActionButton;
10 | import static android.support.test.espresso.action.ViewActions.typeText;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
13 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
14 | import static android.support.test.espresso.matcher.ViewMatchers.withParent;
15 | import static org.hamcrest.Matchers.allOf;
16 | import static org.hamcrest.Matchers.equalTo;
17 |
18 | public class TypesActions {
19 | public void pickFileName(String fileName) {
20 | onView(allOf(
21 | withParent(withId(R.id.pickBar)),
22 | withClassName(equalTo(EditText.class.getName())),
23 | isDisplayed()
24 | )).perform(typeText(fileName));
25 | }
26 |
27 | public void inputFileName(String fileName) {
28 | onView(allOf(
29 | withId(R.id.textinput),
30 | isDisplayed()
31 | )).perform(typeText(fileName));
32 | }
33 |
34 | public void searchQuery(String query) {
35 | onView(allOf(
36 | withId(R.id.searchQuery),
37 | isDisplayed()
38 | )).perform(typeText(query));
39 | }
40 |
41 | public void noSearchQuery() {
42 | onView(allOf(
43 | withId(R.id.searchQuery),
44 | isDisplayed()
45 | )).perform(clearText());
46 | }
47 |
48 | public void imeAction() {
49 | onView(allOf(
50 | withId(R.id.searchQuery),
51 | isDisplayed()
52 | )).perform(pressImeActionButton());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/dialog/SingleDeleteDialog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.dialog;
18 |
19 | import android.app.AlertDialog;
20 | import android.app.Dialog;
21 | import android.os.Bundle;
22 | import android.support.annotation.NonNull;
23 | import android.support.v4.app.DialogFragment;
24 |
25 | import com.veniosg.dir.R;
26 | import com.veniosg.dir.mvvm.model.FileHolder;
27 |
28 | import static com.veniosg.dir.IntentConstants.EXTRA_DIALOG_FILE_HOLDER;
29 |
30 | public class SingleDeleteDialog extends DialogFragment {
31 | @NonNull
32 | @Override
33 | public Dialog onCreateDialog(Bundle savedInstanceState) {
34 | FileHolder holder = getArguments().getParcelable(EXTRA_DIALOG_FILE_HOLDER);
35 |
36 | if (holder != null) {
37 | AlertDialog dialog = new AlertDialog.Builder(getActivity())
38 | .setTitle(getString(R.string.really_delete, holder.getName()))
39 | .setPositiveButton(R.string.yes, (dialog1, which) -> {
40 | new DeleteAsyncTask(getContext()).execute(holder);
41 | })
42 | .setNegativeButton(R.string.no, null)
43 | .create();
44 | dialog.setIcon(R.drawable.ic_dialog_delete);
45 | return dialog;
46 | } else {
47 | dismiss();
48 | return new Dialog(getContext());
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > This project is no longer maintained.
2 |
3 | Dir
4 | ===========
5 |
6 | http://veniosg.github.io/Dir/
7 |
8 | A modern Android file manager.
9 |
10 | [
](https://play.google.com/store/apps/details?id=com.veniosg.dir)
11 | [
](https://f-droid.org/packages/com.veniosg.dir/)
12 |
13 | Contributors
14 | ----
15 | Dir welcomes contributions from developers of all experience levels!
16 |
17 | A good place to start is [this list of issues](https://github.com/veniosg/Dir/labels/good%20first%20issue). Much more interesting (and harder) are [issues marked as high effort](https://github.com/veniosg/Dir/labels/higheffort).
18 |
19 | For direction, help and coordination feel free to comment on the issues themselves or send an email to [dev@pxhouse.co](mailto:dev@pxhouse.co). If you're planning to build a completely new feature please open an issue before starting to ensure that your efforts are not wasted.
20 |
21 | [Visit our Wiki](https://github.com/veniosg/Dir/wiki) for technical documentation.
22 |
23 | Not a developer?
24 | ----
25 | [Join us on OneSky](http://dirapp.oneskyapp.com/collaboration/project?id=27347) and help improve Dir for your language!
26 |
27 | License
28 | --------
29 |
30 | Copyright (C) 2014-2018 George Venios
31 | Copyright (C) 2007-2014 OpenIntents.org
32 |
33 | Licensed under the Apache License, Version 2.0 (the "License");
34 | you may not use this file except in compliance with the License.
35 | You may obtain a copy of the License at
36 |
37 | http://www.apache.org/licenses/LICENSE-2.0
38 |
39 | Unless required by applicable law or agreed to in writing, software
40 | distributed under the License is distributed on an "AS IS" BASIS,
41 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 | See the License for the specific language governing permissions and
43 | limitations under the License.
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/viewmodel/search/SearchViewModel.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.mvvm.viewmodel.search;
2 |
3 | import android.arch.lifecycle.LiveData;
4 | import android.arch.lifecycle.ViewModel;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.VisibleForTesting;
7 |
8 | import com.veniosg.dir.mvvm.model.search.SearchState;
9 | import com.veniosg.dir.mvvm.model.search.Searcher;
10 |
11 | import java.io.File;
12 |
13 | import static com.veniosg.dir.mvvm.model.search.Searcher.SearchRequest.searchRequest;
14 |
15 | public class SearchViewModel extends ViewModel {
16 | private Searcher searcher;
17 | private LiveData liveResults;
18 | private File searchRoot;
19 | private String currentQuery;
20 |
21 | @SuppressWarnings("unused")
22 | public SearchViewModel() {
23 | searcher = new Searcher();
24 | }
25 |
26 | @VisibleForTesting
27 | SearchViewModel(Searcher searcher) {
28 | this.searcher = searcher;
29 | }
30 |
31 | public void init(File searchIn) {
32 | if (liveResults != null) return;
33 |
34 | this.searchRoot = searchIn;
35 | liveResults = searcher.getResults();
36 | }
37 |
38 | /**
39 | * Start a new search, using the passed query. If the current search uses the same query, this does nothing.
40 | * @param query The text to search filenames for.
41 | * @return True if this triggers a new search, false if not.
42 | */
43 | public boolean updateQuery(@NonNull String query) {
44 | if (!query.equals(currentQuery)) {
45 | searcher.updateQuery(searchRequest(searchRoot, query));
46 | currentQuery = query;
47 | return true;
48 | } else {
49 | return false;
50 | }
51 | }
52 |
53 | @Override
54 | protected void onCleared() {
55 | super.onCleared();
56 | // Avoid stray search workers
57 | searcher.stopSearch();
58 | }
59 |
60 | public LiveData getLiveResults() {
61 | return liveResults;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/util/Logger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.util;
18 |
19 | import android.util.Log;
20 |
21 | import com.veniosg.dir.BuildConfig;
22 |
23 | public class Logger {
24 | private static final boolean LOG_ENABLED = BuildConfig.DEBUG;
25 |
26 | private static final String TAG_DEFAULT = "DIR_Default";
27 | public static final String TAG_OBSERVER = "DIR_FileObserver";
28 | public static final String TAG_DIRSCANNER = "DIR_DirectoryScanner";
29 | public static final String TAG_MEDIASCANNER = "DIR_MediaScanner";
30 | public static final String TAG_PATHBAR = "DIR_PathBar";
31 | public static final String TAG_ANIMATION = "DIR_Animation";
32 | public static final String TAG_SEARCH = "DIR_Search";
33 | public static final String TAG_BILLING = "DIR_Billing";
34 |
35 | private Logger(){}
36 |
37 | public static void log(Throwable t) {
38 | if (LOG_ENABLED) {
39 | t.printStackTrace();
40 | }
41 | }
42 |
43 | public static void log(String msg) {
44 | logV(TAG_DEFAULT, msg);
45 | }
46 |
47 | public static void log(int priority, String msg) {
48 | log(priority, TAG_DEFAULT, msg);
49 | }
50 |
51 | public static void logV(String tag, String msg) {
52 | log(Log.VERBOSE, tag, msg);
53 | }
54 |
55 | public static void log(int priority, String tag, String msg) {
56 | if (LOG_ENABLED) {
57 | Log.println(priority, tag, msg);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_eight.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/dialog/MultiDeleteDialog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.dialog;
18 |
19 | import android.app.AlertDialog;
20 | import android.app.Dialog;
21 | import android.os.Bundle;
22 | import android.support.annotation.NonNull;
23 | import android.support.v4.app.DialogFragment;
24 |
25 | import com.veniosg.dir.R;
26 | import com.veniosg.dir.mvvm.model.FileHolder;
27 |
28 | import java.util.ArrayList;
29 |
30 | import static com.veniosg.dir.IntentConstants.EXTRA_DIALOG_FILE_HOLDER;
31 |
32 | public class MultiDeleteDialog extends DialogFragment {
33 | @NonNull
34 | @Override
35 | public Dialog onCreateDialog(Bundle savedInstanceState) {
36 | ArrayList holders = getArguments().getParcelableArrayList(EXTRA_DIALOG_FILE_HOLDER);
37 |
38 | if (holders != null) {
39 | AlertDialog dialog = new AlertDialog.Builder(getActivity())
40 | .setTitle(getString(R.string.really_delete_multiselect, holders.size()))
41 | .setPositiveButton(R.string.yes, (dialog1, which) -> {
42 | FileHolder[] params = holders.toArray(new FileHolder[holders.size()]);
43 | new DeleteAsyncTask(getContext()).execute(params);
44 | })
45 | .setNegativeButton(R.string.no, null)
46 | .create();
47 | dialog.setIcon(R.drawable.ic_dialog_delete);
48 | return dialog;
49 | } else {
50 | dismiss();
51 | return new Dialog(getContext());
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/acceptance/InternalStorageOperationsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.test.acceptance;
18 |
19 | import android.content.Context;
20 | import android.os.Environment;
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 |
25 | import static com.veniosg.dir.test.TestUtils.cleanDirectory;
26 |
27 | public class InternalStorageOperationsTest extends FileOperationsTest {
28 | @Override
29 | protected File getStorageRoot(Context context) {
30 | return Environment.getExternalStorageDirectory();
31 | }
32 |
33 | @SuppressWarnings("ResultOfMethodCallIgnored")
34 | @Override
35 | protected void setUpFiles(Context context, File testDirectory, File testChildDirectory,
36 | File testCopyDestination, File testChildFile) throws IOException {
37 | testDirectory.mkdir();
38 | testChildDirectory.mkdir();
39 | testCopyDestination.mkdir();
40 | testChildFile.createNewFile();
41 | }
42 |
43 | @SuppressWarnings("ResultOfMethodCallIgnored")
44 | @Override
45 | protected void tearDownFiles(Context context, File testExtractedDirectory, File testDirectory, File compressedFile) {
46 | if (testExtractedDirectory.exists()) {
47 | testExtractedDirectory.renameTo(new File(testDirectory, "extracted"));
48 | }
49 | if (compressedFile.exists()) {
50 | compressedFile.renameTo(new File(testDirectory, "compressed"));
51 | }
52 | cleanDirectory(testDirectory);
53 | testDirectory.delete();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_paste_more.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/adapter/FileListViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.android.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import com.veniosg.dir.R;
11 | import com.veniosg.dir.mvvm.model.FileHolder;
12 |
13 | import java.io.File;
14 |
15 | import static android.view.LayoutInflater.from;
16 | import static com.veniosg.dir.android.misc.ThumbnailHelper.requestIcon;
17 | import static com.veniosg.dir.android.ui.Themer.getThemedResourceId;
18 |
19 | public class FileListViewHolder extends RecyclerView.ViewHolder {
20 | private ImageView icon;
21 | private TextView primaryInfo;
22 | TextView secondaryInfo;
23 | private TextView tertiaryInfo;
24 |
25 | FileListViewHolder(ViewGroup parent) {
26 | super(from(parent.getContext()).inflate(R.layout.item_filelist, parent, false));
27 |
28 | icon = (ImageView) itemView.findViewById(R.id.icon);
29 | primaryInfo = (TextView) itemView.findViewById(R.id.primary_info);
30 | secondaryInfo = (TextView) itemView.findViewById(R.id.secondary_info);
31 | tertiaryInfo = (TextView) itemView.findViewById(R.id.tertiary_info);
32 |
33 | int selectorRes = getThemedResourceId(parent.getContext(), android.R.attr.listChoiceBackgroundIndicator);
34 | itemView.setBackgroundResource(selectorRes);
35 | }
36 |
37 | void bind(String filePath, OnItemClickListener listener) {
38 | Context context = itemView.getContext();
39 | FileHolder item = new FileHolder(new File(filePath), context);
40 | boolean isDirectory = item.getFile().isDirectory();
41 |
42 | primaryInfo.setText(item.getName());
43 | secondaryInfo.setText(item.getFormattedModificationDate(context));
44 | tertiaryInfo.setText(isDirectory ? "" : item.getFormattedSize(context, false));
45 | icon.setImageDrawable(item.getBestIcon());
46 | requestIcon(item, icon);
47 |
48 | itemView.setOnClickListener(view -> listener.onClick(itemView, item));
49 | }
50 |
51 | public interface OnItemClickListener {
52 | public void onClick(View itemView, FileHolder item);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_about_github.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 | #8888
22 |
23 |
24 |
25 |
26 | #8e24aa
27 | #6a1b9a
28 | #7b1fa2
29 | #00BFA5
30 | #EEEEEE
31 | #424242
32 | #212121
33 | #E0E0E0
34 | #757575
35 |
36 | #BB222222
37 |
38 |
39 |
40 | #000
41 | #181818
42 |
43 |
44 |
45 |
46 | #212121
47 | #000
48 | #424242
49 | #f5f5f5
50 | #303030
51 | #424242
52 | #212121
53 | #2B2B2B
54 | #9E9E9E
55 |
56 | #B000
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/cab_multi.xml:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/activity/SearchActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2017 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.activity;
18 |
19 | import android.content.Intent;
20 | import android.os.Bundle;
21 | import android.view.MenuItem;
22 |
23 | import com.veniosg.dir.android.fragment.SearchListFragment;
24 |
25 | import static com.veniosg.dir.android.util.Utils.showHome;
26 |
27 | public class SearchActivity extends BaseActivity {
28 | private SearchListFragment mFragment;
29 |
30 | @Override
31 | protected void onNewIntent(Intent intent) {
32 | setIntent(intent);
33 | handleRequest();
34 | }
35 |
36 | protected void onCreate(Bundle savedInstanceState) {
37 | // Presentation settings
38 | super.onCreate(savedInstanceState);
39 |
40 | setupToolbar();
41 |
42 | // Handle the search request.
43 | handleRequest();
44 | }
45 |
46 | private void handleRequest() {
47 | // Add fragment only if it hasn't already been added.
48 | mFragment = (SearchListFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
49 | if (mFragment == null) {
50 | mFragment = new SearchListFragment();
51 | getSupportFragmentManager()
52 | .beginTransaction()
53 | .replace(android.R.id.content, mFragment, FRAGMENT_TAG)
54 | .commit();
55 | }
56 | }
57 |
58 | @Override
59 | public boolean onOptionsItemSelected(MenuItem item) {
60 | switch (item.getItemId()) {
61 | case android.R.id.home:
62 | showHome(this);
63 | break;
64 | }
65 | return super.onOptionsItemSelected(item);
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/state_list_anim_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 | -
18 |
19 |
23 |
27 |
28 |
29 |
30 | -
31 |
32 |
37 |
41 |
42 |
43 | -
44 |
45 |
49 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
28 |
29 |
33 |
34 |
35 |
40 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/FileOperation.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.mvvm.model.storage.operation;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import java.io.File;
6 |
7 | import static java.util.UUID.randomUUID;
8 |
9 | public abstract class FileOperation {
10 | protected final int id = randomUUID().hashCode();
11 |
12 | /**
13 | * @return Whether the operation was successful.
14 | */
15 | public abstract boolean operate(A args);
16 |
17 | /**
18 | * Try the operation using SAF facilities
19 | * Triggered if {@link #operate(Arguments)} returns false and we have write permissions.
20 | *
21 | * @return Whether the operation was successful.
22 | */
23 | public abstract boolean operateSaf(A args);
24 |
25 | /**
26 | * Good place to show initial UI, or prepare any dialogs etc.
27 | * Called right before running the operation. Can be called multiple times.
28 | *
29 | * @param args Original arguments for the invocation that is getting started.
30 | */
31 | abstract void onStartOperation(A args);
32 |
33 | /**
34 | * Good place to show final result (success/failure) UI.
35 | * No other callbacks will happen after this.
36 | *
37 | * @param success Whether the invocation was successful.
38 | * @param args Original arguments for the invocation that just finished.
39 | */
40 | abstract void onResult(boolean success, A args);
41 |
42 | /**
43 | * Called if the user has denied storage write permissions on the parent volume of {@link Arguments#target}.
44 | * No other callbacks will happen after this.
45 | */
46 | abstract void onAccessDenied();
47 |
48 | /**
49 | * Good place to hide any progress UI. You may still get calls to onResult()/onAccessDenied() after this.
50 | */
51 | abstract void onRequestingAccess();
52 |
53 | /**
54 | * @return Whether this type of operation can fail because of lack of storage write permissions.
55 | */
56 | public abstract boolean needsWriteAccess();
57 |
58 | public static abstract class Arguments {
59 | @NonNull
60 | private final File target;
61 |
62 | protected Arguments(@NonNull File target) {
63 | this.target = target;
64 | }
65 |
66 | @NonNull
67 | protected File getTarget() {
68 | return target;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/ui/widget/WaitingViewFlipper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2015 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.ui.widget;
18 |
19 | import android.content.Context;
20 | import android.os.Handler;
21 | import android.util.AttributeSet;
22 | import android.widget.ViewFlipper;
23 |
24 | import static com.veniosg.dir.AnimationConstants.ANIM_START_DELAY;
25 |
26 | /**
27 | * Children must always follow the PAGE_INDEX_* indexing.
28 | */
29 | public class WaitingViewFlipper extends ViewFlipper {
30 | public static final int PAGE_INDEX_CONTENT = 0;
31 | public static final int PAGE_INDEX_LOADING = 1;
32 | public static final int PAGE_INDEX_PERMISSION_DENIED = 2;
33 |
34 | private Handler mWaiterHandler = new Handler();
35 | private Runnable mWaiter;
36 | private int mWaitingChild = -1;
37 |
38 | public WaitingViewFlipper(Context context) {
39 | super(context);
40 | }
41 |
42 | public WaitingViewFlipper(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | }
45 |
46 | public void setDisplayedChildDelayed(final int child) {
47 | if (mWaiter != null) {
48 | mWaiterHandler.removeCallbacks(mWaiter);
49 | }
50 |
51 | mWaiter = new Runnable() {
52 | @Override
53 | public void run() {
54 | WaitingViewFlipper.this.setDisplayedChild(child);
55 | }
56 | };
57 | mWaiterHandler.postDelayed(mWaiter, ANIM_START_DELAY);
58 | mWaitingChild = child;
59 | }
60 |
61 | public void setDisplayedChild(int whichChild) {
62 | if (mWaitingChild != -1) {
63 | mWaitingChild = -1;
64 | mWaiterHandler.removeCallbacks(mWaiter);
65 | }
66 |
67 | if (getDisplayedChild() != whichChild)
68 | super.setDisplayedChild(whichChild);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/acceptance/SdCardOperationsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.test.acceptance;
18 |
19 | import android.content.Context;
20 |
21 | import com.veniosg.dir.android.util.FileUtils;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 | import java.util.List;
26 |
27 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createDirectory;
28 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createFile;
29 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.safAwareDelete;
30 | import static com.veniosg.dir.test.TestUtils.cleanDirectorySaf;
31 |
32 | public class SdCardOperationsTest extends FileOperationsTest {
33 | @Override
34 | protected File getStorageRoot(Context context) {
35 | List extPaths = FileUtils.getExtSdCardPaths(context);
36 | if (extPaths.isEmpty()) {
37 | throw new IllegalStateException("Can't test SD Card operations without an SD Card attached.");
38 | }
39 | return new File(extPaths.get(0));
40 | }
41 |
42 | @Override
43 | protected void setUpFiles(Context context, File testDirectory, File testChildDirectory, File testCopyDestination, File testChildFile) throws IOException {
44 | createDirectory(context, testDirectory);
45 | createDirectory(context, testChildDirectory);
46 | createDirectory(context, testCopyDestination);
47 | createFile(context, testChildFile, "*/*");
48 | }
49 |
50 | @Override
51 | protected void tearDownFiles(Context context, File testExtractedDirectory, File testDirectory, File compressedFile) {
52 | safAwareDelete(context, testExtractedDirectory);
53 | safAwareDelete(context, compressedFile);
54 | cleanDirectorySaf(context, testDirectory);
55 | safAwareDelete(context, testDirectory);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/adapter/SearchListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.adapter;
18 |
19 | import android.support.annotation.NonNull;
20 | import android.support.v7.widget.RecyclerView;
21 | import android.view.ViewGroup;
22 |
23 | import com.veniosg.dir.android.adapter.FileListViewHolder.OnItemClickListener;
24 |
25 | import java.util.List;
26 |
27 | import static com.veniosg.dir.android.util.Utils.firstDifferentItemIndex;
28 | import static java.util.Collections.emptyList;
29 |
30 | public class SearchListAdapter extends RecyclerView.Adapter {
31 | private List data = emptyList();
32 | private OnItemClickListener onItemClickListener;
33 |
34 | public SearchListAdapter(OnItemClickListener onItemClickListener) {
35 | this.onItemClickListener = onItemClickListener;
36 | }
37 |
38 | public void notifyDataUpdated(@NonNull List updatedData) {
39 | int firstUpdatedIndex = firstDifferentItemIndex(data, updatedData);
40 | if (firstUpdatedIndex != -1) { // Lists are not equal
41 | int oldCount = data.size();
42 | int newCount = updatedData.size();
43 | data = updatedData;
44 |
45 | if (firstUpdatedIndex == oldCount) { // Data appended
46 | notifyItemRangeInserted(firstUpdatedIndex, updatedData.size() - oldCount);
47 | } else {
48 | notifyDataSetChanged();
49 | }
50 | }
51 | }
52 |
53 | @Override
54 | public SearchListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
55 | return new SearchListViewHolder(parent);
56 | }
57 |
58 | @Override
59 | public void onBindViewHolder(SearchListViewHolder holder, int position) {
60 | holder.bind(data.get(position), onItemClickListener);
61 | }
62 |
63 | @Override
64 | public int getItemCount() {
65 | return data.size();
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/options_filelist_simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
26 |
33 | -
41 |
42 | -
49 |
50 | -
56 |
57 | -
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/ui/widget/PathContainerLayout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.ui.widget;
18 |
19 | import android.content.Context;
20 | import android.util.AttributeSet;
21 | import android.view.View;
22 |
23 | import static android.view.View.MeasureSpec.AT_MOST;
24 | import static android.view.View.MeasureSpec.UNSPECIFIED;
25 | import static android.view.View.MeasureSpec.makeMeasureSpec;
26 |
27 | public class PathContainerLayout extends ChildrenChangedListeningLinearLayout {
28 | private int mMaxChildWidth = 0;
29 |
30 | public PathContainerLayout(Context context) {
31 | super(context);
32 | }
33 |
34 | public PathContainerLayout(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 | public PathContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
39 | super(context, attrs, defStyleAttr);
40 | }
41 |
42 | void setMeasuredWidth(int width) {
43 | setMeasuredDimension(width, getMeasuredHeight());
44 | }
45 |
46 | @Override
47 | protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
48 | int childWidthSpec = makeMeasureSpec(0, UNSPECIFIED);
49 | if (mMaxChildWidth > 0) {
50 | childWidthSpec = makeMeasureSpec(mMaxChildWidth, AT_MOST);
51 | }
52 |
53 | child.measure(childWidthSpec, parentHeightMeasureSpec);
54 | }
55 |
56 | /**
57 | * Set the maximum width that this layout's children are allowed to have.
58 | *
59 | * @param maxChildWidthPx Negative or zero to cancel, positive to set the max width.
60 | */
61 | @SuppressWarnings("WeakerAccess")
62 | public void setMaxChildWidth(int maxChildWidthPx) {
63 | this.mMaxChildWidth = maxChildWidthPx;
64 | }
65 |
66 | @SuppressWarnings("unused")
67 | public int getMaxChildWidth() {
68 | return mMaxChildWidth;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/mimetypes.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/test/java/com/veniosg/dir/mvvm/viewmodel/search/SearchViewModelTest.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.mvvm.viewmodel.search;
2 |
3 | import android.arch.lifecycle.LiveData;
4 | import android.arch.lifecycle.MutableLiveData;
5 |
6 | import com.veniosg.dir.mvvm.model.search.SearchState;
7 | import com.veniosg.dir.mvvm.model.search.Searcher;
8 | import com.veniosg.dir.mvvm.viewmodel.search.SearchViewModel;
9 |
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.mockito.Mock;
13 |
14 | import java.io.File;
15 |
16 | import static com.veniosg.dir.mvvm.model.search.Searcher.SearchRequest.searchRequest;
17 | import static junit.framework.Assert.assertEquals;
18 | import static junit.framework.Assert.assertFalse;
19 | import static junit.framework.Assert.assertTrue;
20 | import static org.mockito.ArgumentMatchers.refEq;
21 | import static org.mockito.Mockito.verify;
22 | import static org.mockito.Mockito.when;
23 | import static org.mockito.MockitoAnnotations.initMocks;
24 |
25 | public class SearchViewModelTest {
26 | @Mock
27 | private Searcher mockSearcher;
28 | private SearchViewModel viewModel;
29 | private final File searchRoot = new File("/");
30 |
31 | @Before
32 | public void setUp() {
33 | initMocks(this);
34 | viewModel = new SearchViewModel(mockSearcher);
35 | }
36 |
37 | @Test
38 | public void delegatesSearchResultsToSearcher() {
39 | LiveData searcherLiveResults = new MutableLiveData<>();
40 | when(mockSearcher.getResults()).thenReturn(searcherLiveResults);
41 |
42 | viewModel.init(searchRoot);
43 |
44 | assertEquals(searcherLiveResults, viewModel.getLiveResults());
45 | }
46 |
47 | @Test
48 | public void onClearedCleansUp() {
49 | viewModel.onCleared();
50 |
51 | verify(mockSearcher).stopSearch();
52 | }
53 |
54 | @Test
55 | public void updateQueryDelegatesToSearcher() {
56 | String newQuery = "query2";
57 | viewModel.init(searchRoot);
58 |
59 | viewModel.updateQuery(newQuery);
60 |
61 | verify(mockSearcher).updateQuery(refEq(searchRequest(searchRoot, newQuery)));
62 | }
63 |
64 | @Test
65 | public void onlyStartsOneSearchForSameQuery() {
66 | String query = "query";
67 | viewModel.init(searchRoot);
68 |
69 | boolean firstSearchStarted = viewModel.updateQuery(query);
70 | boolean secondSearchStarted = viewModel.updateQuery(query);
71 |
72 | assertTrue(firstSearchStarted);
73 | assertFalse(secondSearchStarted);
74 | verify(mockSearcher).updateQuery(refEq(searchRequest(searchRoot, query)));
75 | }
76 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/CreateDirectoryOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.mvvm.model.storage.operation;
18 |
19 | import android.content.Context;
20 |
21 | import com.veniosg.dir.android.fragment.FileListFragment;
22 | import com.veniosg.dir.android.ui.toast.ToastDisplayer;
23 | import com.veniosg.dir.mvvm.model.storage.operation.argument.CreateDirectoryArguments;
24 |
25 | import java.io.File;
26 |
27 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createDirectory;
28 |
29 | public class CreateDirectoryOperation extends FileOperation {
30 | private final Context context;
31 | private final ToastDisplayer toastDisplayer;
32 |
33 | public CreateDirectoryOperation(Context context) {
34 | this.context = context;
35 | this.toastDisplayer = new ToastDisplayer(context);
36 | }
37 |
38 | @Override
39 | public boolean operate(CreateDirectoryArguments args) {
40 | File dest = args.getTarget();
41 |
42 | return dest.exists() || dest.mkdirs();
43 | }
44 |
45 | @Override
46 | public boolean operateSaf(CreateDirectoryArguments args) {
47 | File dest = args.getTarget();
48 |
49 | return dest.exists() || createDirectory(context, dest) != null;
50 | }
51 |
52 | @Override
53 | public void onStartOperation(CreateDirectoryArguments args) {
54 | }
55 |
56 | @Override
57 | public void onResult(boolean success, CreateDirectoryArguments args) {
58 | if (success) {
59 | toastDisplayer.createDirectorySuccess();
60 | FileListFragment.refresh(context, args.getTarget().getParentFile());
61 | } else {
62 | toastDisplayer.createDirectoryFailure();
63 | }
64 | }
65 |
66 | @Override
67 | public void onAccessDenied() {
68 | }
69 |
70 | @Override
71 | public void onRequestingAccess() {
72 | }
73 |
74 | @Override
75 | public boolean needsWriteAccess() {
76 | return true;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_filelist_pick.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 |
22 |
29 |
30 |
35 |
36 |
43 |
44 |
45 |
51 |
52 |
53 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/test/java/com/veniosg/dir/mvvm/model/storage/operation/FakeStorageAccessManager.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.mvvm.model.storage.operation;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.veniosg.dir.mvvm.model.storage.access.StorageAccessManager;
6 |
7 | import java.io.File;
8 |
9 |
10 | @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"})
11 | public class FakeStorageAccessManager implements StorageAccessManager {
12 | private enum Behavior {
13 | GRANT,
14 | DENY,
15 | ERROR
16 | }
17 |
18 | private Behavior behavior = Behavior.ERROR;
19 | private boolean writeAccess = false;
20 | private boolean safBased = false;
21 | private boolean listened = true;
22 |
23 | public static FakeStorageAccessManager aFakeStorageAccessManager() {
24 | return new FakeStorageAccessManager();
25 | }
26 |
27 | public FakeStorageAccessManager thatAlwaysGrantsAccess() {
28 | behavior = Behavior.GRANT;
29 | listened = false;
30 | return this;
31 | }
32 |
33 | public FakeStorageAccessManager thatAlwaysDeniesAccess() {
34 | behavior = Behavior.DENY;
35 | listened = false;
36 | return this;
37 | }
38 |
39 | public FakeStorageAccessManager thatAlwaysErrors() {
40 | behavior = Behavior.ERROR;
41 | listened = false;
42 | return this;
43 | }
44 |
45 | public FakeStorageAccessManager thatHasNoWriteAccess() {
46 | writeAccess = false;
47 | return this;
48 | }
49 |
50 | public FakeStorageAccessManager thatHasWriteAccess() {
51 | writeAccess = true;
52 | return this;
53 | }
54 |
55 | public FakeStorageAccessManager thatIsSafBased() {
56 | safBased = true;
57 | return this;
58 | }
59 |
60 | @Override
61 | public boolean hasWriteAccess(@NonNull File fileInStorage) {
62 | return writeAccess;
63 | }
64 |
65 | @Override
66 | public void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener) {
67 | if (!listened) {
68 | listened = true;
69 | switch (behavior) {
70 | case GRANT:
71 | writeAccess = true;
72 | listener.granted();
73 | break;
74 | case DENY:
75 | writeAccess = false;
76 | listener.denied();
77 | break;
78 | case ERROR:
79 | writeAccess = false;
80 | listener.error();
81 | break;
82 | }
83 | }
84 | }
85 |
86 | @Override
87 | public boolean isSafBased() {
88 | return safBased;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.activity;
18 |
19 | import android.content.BroadcastReceiver;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.content.IntentFilter;
23 | import android.os.Bundle;
24 | import android.support.v4.app.FragmentActivity;
25 | import android.support.v4.content.LocalBroadcastManager;
26 | import android.widget.Toolbar;
27 |
28 | import com.veniosg.dir.R;
29 | import com.veniosg.dir.android.ui.Themer;
30 |
31 | import static com.veniosg.dir.IntentConstants.ACTION_REFRESH_THEME;
32 |
33 | abstract class BaseActivity extends FragmentActivity {
34 | static final String FRAGMENT_TAG = "content_fragment";
35 |
36 | private final BroadcastReceiver mThemeReceiver = new BroadcastReceiver() {
37 | @Override
38 | public void onReceive(Context context, Intent intent) {
39 | if (PreferenceActivity.class.equals(BaseActivity.this.getClass())) {
40 | finish();
41 | startActivity(getIntent());
42 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
43 | } else {
44 | recreate();
45 | }
46 | }
47 | };
48 |
49 | @Override
50 | protected void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 |
53 | Themer.applyTheme(this);
54 |
55 | LocalBroadcastManager.getInstance(this)
56 | .registerReceiver(mThemeReceiver, new IntentFilter(ACTION_REFRESH_THEME));
57 | }
58 |
59 | @Override
60 | protected void onDestroy() {
61 | LocalBroadcastManager.getInstance(this).unregisterReceiver(mThemeReceiver);
62 | super.onDestroy();
63 | }
64 |
65 | /**
66 | * Always call this after setContent() or it will not have any effect.
67 | */
68 | protected void setupToolbar() {
69 | Toolbar toolbar = findViewById(R.id.toolbar);
70 | if (toolbar != null) {
71 | setActionBar(toolbar);
72 | //noinspection ConstantConditions
73 | getActionBar().setDisplayHomeAsUpEnabled(true);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/veniosg/dir/android/adapter/BookmarkListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 George Venios
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.veniosg.dir.android.adapter;
18 |
19 | import android.content.Context;
20 | import android.database.Cursor;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.CursorAdapter;
25 | import android.widget.ImageView;
26 | import android.widget.TextView;
27 |
28 | import com.veniosg.dir.R;
29 | import com.veniosg.dir.mvvm.model.FileHolder;
30 | import com.veniosg.dir.android.misc.ThumbnailHelper;
31 | import com.veniosg.dir.android.ui.ViewHolder;
32 |
33 | import java.io.File;
34 |
35 | import static android.view.View.INVISIBLE;
36 |
37 | public class BookmarkListAdapter extends CursorAdapter {
38 | public BookmarkListAdapter(Context context, Cursor cursor){
39 | super(context, cursor, 0);
40 | }
41 |
42 | @Override
43 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
44 | ViewHolder viewHolder;
45 | View view = LayoutInflater.from(context).inflate(R.layout.item_filelist, null);
46 |
47 | viewHolder = new ViewHolder();
48 | viewHolder.icon = (ImageView) view.findViewById(R.id.icon);
49 | viewHolder.primaryInfo = (TextView) view.findViewById(R.id.primary_info);
50 | viewHolder.secondaryInfo = (TextView) view.findViewById(R.id.secondary_info);
51 | viewHolder.tertiaryInfo = (TextView) view.findViewById(R.id.tertiary_info);
52 | view.setTag(viewHolder);
53 |
54 | return view;
55 | }
56 |
57 | @Override
58 | public void bindView(View convertView, Context context, Cursor cursor) {
59 | ViewHolder holder = (com.veniosg.dir.android.ui.ViewHolder) convertView.getTag();
60 | FileHolder item = new FileHolder(new File(cursor.getString(2)), convertView.getContext());
61 |
62 | holder.icon.setImageDrawable(item.getBestIcon());
63 | holder.primaryInfo.setText(item.getName());
64 | holder.secondaryInfo.setMaxLines(3);
65 | holder.secondaryInfo.setText(item.getFile().getAbsolutePath());
66 | holder.tertiaryInfo.setVisibility(INVISIBLE);
67 |
68 | ThumbnailHelper.requestIcon(item, holder.icon);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/veniosg/dir/test/actor/assertion/CannotSeeAssertions.java:
--------------------------------------------------------------------------------
1 | package com.veniosg.dir.test.actor.assertion;
2 |
3 | import android.support.test.espresso.assertion.ViewAssertions;
4 | import android.view.View;
5 | import android.widget.Adapter;
6 | import android.widget.AdapterView;
7 |
8 | import com.veniosg.dir.R;
9 |
10 | import org.hamcrest.Description;
11 | import org.hamcrest.Matcher;
12 | import org.hamcrest.TypeSafeMatcher;
13 |
14 | import java.io.File;
15 |
16 | import static android.support.test.espresso.Espresso.onView;
17 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
18 | import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
19 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
20 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
21 | import static android.support.test.espresso.matcher.ViewMatchers.withText;
22 | import static com.veniosg.dir.test.matcher.FileHolderHasName.hasName;
23 | import static org.hamcrest.Matchers.allOf;
24 | import static org.hamcrest.Matchers.not;
25 |
26 | public class CannotSeeAssertions {
27 | public void fileInList(File file) {
28 | String filename = file.getName();
29 | onView(allOf(
30 | withId(android.R.id.list),
31 | isDescendantOfA(withId(R.id.zoomview))
32 | )).check(matches(not(withAdaptedData(hasName(filename)))));
33 | }
34 |
35 | public void visibleSearchResult(File result) {
36 | onView(allOf(
37 | withId(R.id.primary_info),
38 | withText(result.getName())
39 | )).check(ViewAssertions.doesNotExist());
40 | }
41 |
42 | public void searchEmptyView() {
43 | onView(withId(R.id.empty_text))
44 | .check(matches(not(isDisplayed())));
45 | onView(withId(R.id.empty_img))
46 | .check(matches(not(isDisplayed())));
47 | }
48 |
49 | private static Matcher withAdaptedData(final Matcher