• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
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.android.documentsui;
18 
19 import static android.content.ContentResolver.wrap;
20 
21 import static com.android.documentsui.base.SharedMinimal.TAG;
22 
23 import android.app.Dialog;
24 import android.content.ContentProviderClient;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.DialogInterface.OnClickListener;
29 import android.net.Uri;
30 import android.os.AsyncTask;
31 import android.os.Bundle;
32 import android.os.FileUtils;
33 import android.provider.DocumentsContract;
34 import android.provider.DocumentsContract.Document;
35 import android.util.Log;
36 import android.view.KeyEvent;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.inputmethod.EditorInfo;
40 import android.widget.EditText;
41 import android.widget.TextView;
42 import android.widget.TextView.OnEditorActionListener;
43 
44 import androidx.annotation.Nullable;
45 import androidx.appcompat.app.AlertDialog;
46 import androidx.fragment.app.DialogFragment;
47 import androidx.fragment.app.FragmentManager;
48 
49 import com.android.documentsui.base.DocumentInfo;
50 import com.android.documentsui.base.Shared;
51 import com.android.documentsui.ui.Snackbars;
52 
53 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
54 import com.google.android.material.snackbar.Snackbar;
55 import com.google.android.material.textfield.TextInputLayout;
56 
57 /**
58  * Dialog to create a new directory.
59  */
60 public class CreateDirectoryFragment extends DialogFragment {
61     private static final String TAG_CREATE_DIRECTORY = "create_directory";
62 
show(FragmentManager fm)63     public static void show(FragmentManager fm) {
64         if (fm.isStateSaved()) {
65             Log.w(TAG, "Skip show create folder dialog because state saved");
66             return;
67         }
68 
69         final CreateDirectoryFragment dialog = new CreateDirectoryFragment();
70         dialog.show(fm, TAG_CREATE_DIRECTORY);
71     }
72 
73     @Override
onCreateDialog(Bundle savedInstanceState)74     public Dialog onCreateDialog(Bundle savedInstanceState) {
75         final Context context = getActivity();
76 
77         final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
78         final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
79 
80         final View view = dialogInflater.inflate(R.layout.dialog_file_name, null, false);
81         final EditText editText = (EditText) view.findViewById(android.R.id.text1);
82 
83         final TextInputLayout inputWrapper = view.findViewById(R.id.input_wrapper);
84         inputWrapper.setHint(getString(R.string.input_hint_new_folder));
85 
86         builder.setTitle(R.string.menu_create_dir);
87         builder.setView(view);
88 
89         builder.setPositiveButton(
90                 android.R.string.ok,
91                 new OnClickListener() {
92                     @Override
93                     public void onClick(DialogInterface dialog, int which) {
94                         createDirectory(editText.getText().toString());
95                     }
96                 });
97 
98         builder.setNegativeButton(android.R.string.cancel, null);
99         final AlertDialog dialog = builder.create();
100 
101         // Workaround for the problem - virtual keyboard doesn't show on the phone.
102         Shared.ensureKeyboardPresent(context, dialog);
103 
104         editText.setOnEditorActionListener(
105                 new OnEditorActionListener() {
106                     @Override
107                     public boolean onEditorAction(
108                             TextView view, int actionId, @Nullable KeyEvent event) {
109                         if ((actionId == EditorInfo.IME_ACTION_DONE) || (event != null
110                                 && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
111                                 && event.hasNoModifiers())) {
112                             createDirectory(editText.getText().toString());
113                             dialog.dismiss();
114                             return true;
115                         }
116                         return false;
117                     }
118                 });
119         editText.requestFocus();
120 
121         return dialog;
122     }
123 
createDirectory(String name)124     private void createDirectory(String name) {
125         final BaseActivity activity = (BaseActivity) getActivity();
126         final DocumentInfo cwd = activity.getCurrentDirectory();
127 
128         new CreateDirectoryTask(activity, cwd, name).executeOnExecutor(
129                 ProviderExecutor.forAuthority(cwd.authority));
130     }
131 
132     private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
133         private final BaseActivity mActivity;
134         private final DocumentInfo mCwd;
135         private final String mDisplayName;
136 
CreateDirectoryTask( BaseActivity activity, DocumentInfo cwd, String displayName)137         public CreateDirectoryTask(
138                 BaseActivity activity, DocumentInfo cwd, String displayName) {
139             mActivity = activity;
140             mCwd = cwd;
141             mDisplayName = displayName;
142         }
143 
144         @Override
doInBackground(Void... params)145         protected DocumentInfo doInBackground(Void... params) {
146             final ContentResolver resolver = mCwd.userId.getContentResolver(mActivity);
147             ContentProviderClient client = null;
148             try {
149                 client = DocumentsApplication.acquireUnstableProviderOrThrow(
150                         resolver, mCwd.derivedUri.getAuthority());
151                 final Uri childUri = DocumentsContract.createDocument(
152                         wrap(client), mCwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
153                 DocumentInfo doc = DocumentInfo.fromUri(resolver, childUri, mCwd.userId);
154                 return doc.isDirectory() ? doc : null;
155             } catch (Exception e) {
156                 Log.w(TAG, "Failed to create directory", e);
157                 return null;
158             } finally {
159                 FileUtils.closeQuietly(client);
160             }
161         }
162 
163         @Override
onPostExecute(DocumentInfo result)164         protected void onPostExecute(DocumentInfo result) {
165             if (result != null) {
166                 // Navigate into newly created child
167                 mActivity.onDirectoryCreated(result);
168                 Metrics.logCreateDirOperation();
169             } else {
170                 Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_LONG)
171                         .show();
172                 Metrics.logCreateDirError();
173             }
174         }
175     }
176 }
177