• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.picker;
18 
19 import static com.android.documentsui.services.FileOperationService.OPERATION_COMPRESS;
20 import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
21 import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
22 import static com.android.documentsui.services.FileOperationService.OPERATION_EXTRACT;
23 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
24 import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN;
25 import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
26 
27 import android.content.pm.PackageManager;
28 import android.os.Bundle;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.Button;
33 
34 import androidx.fragment.app.Fragment;
35 import androidx.fragment.app.FragmentActivity;
36 import androidx.fragment.app.FragmentManager;
37 import androidx.fragment.app.FragmentTransaction;
38 
39 import com.android.documentsui.BaseActivity;
40 import com.android.documentsui.Injector;
41 import com.android.documentsui.R;
42 import com.android.documentsui.base.DocumentInfo;
43 import com.android.documentsui.base.State;
44 import com.android.documentsui.services.FileOperationService.OpType;
45 import com.android.documentsui.ui.Snackbars;
46 
47 import com.google.android.material.snackbar.Snackbar;
48 
49 /**
50  * Display pick confirmation bar, usually for selecting a directory.
51  */
52 public class PickFragment extends Fragment {
53     public static final String TAG = "PickFragment";
54 
55     private static final String ACTION_KEY = "action";
56     private static final String COPY_OPERATION_SUBTYPE_KEY = "copyOperationSubType";
57     private static final String PICK_TARGET_KEY = "pickTarget";
58     private static final String RESTRICT_SCOPE_STORAGE_KEY = "restrictScopeStorage";
59 
60     private final View.OnClickListener mPickListener = new View.OnClickListener() {
61         @Override
62         public void onClick(View v) {
63             if (mPick.isEnabled()) {
64                 mInjector.actions.pickDocument(getChildFragmentManager(), mPickTarget);
65             } else {
66                 String msg = getResources().getString(R.string.directory_blocked_header_subtitle);
67                 Snackbars.makeSnackbar(getActivity(), msg, Snackbar.LENGTH_LONG).show();
68             }
69         }
70     };
71 
72     private final View.OnClickListener mCancelListener = new View.OnClickListener() {
73         @Override
74         public void onClick(View v) {
75             mInjector.pickResult.increaseActionCount();
76             final BaseActivity activity = BaseActivity.get(PickFragment.this);
77             activity.setResult(FragmentActivity.RESULT_CANCELED);
78             activity.finish();
79         }
80     };
81 
82     private Injector<ActionHandler<PickActivity>> mInjector;
83     private int mAction;
84     private boolean mRestrictScopeStorage;
85     // Only legal values are OPERATION_COPY, OPERATION_COMPRESS, OPERATION_EXTRACT,
86     // OPERATION_MOVE, and unset (OPERATION_UNKNOWN).
87     private @OpType int mCopyOperationSubType = OPERATION_UNKNOWN;
88     private DocumentInfo mPickTarget;
89     private View mContainer;
90     private View mPickOverlay;
91     private Button mPick;
92     private Button mCancel;
93 
show(FragmentManager fm)94     public static void show(FragmentManager fm) {
95         // Fragment can be restored by FragmentManager automatically.
96         if (get(fm) != null) {
97             return;
98         }
99 
100         final PickFragment fragment = new PickFragment();
101         final FragmentTransaction ft = fm.beginTransaction();
102         ft.replace(R.id.container_save, fragment, TAG);
103         ft.commitNowAllowingStateLoss();
104     }
105 
get(FragmentManager fm)106     public static PickFragment get(FragmentManager fm) {
107         return (PickFragment) fm.findFragmentByTag(TAG);
108     }
109 
110     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)111     public View onCreateView(
112             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
113         mContainer = inflater.inflate(R.layout.fragment_pick, container, false);
114 
115         mPick = (Button) mContainer.findViewById(android.R.id.button1);
116         mPickOverlay = mContainer.findViewById((R.id.pick_button_overlay));
117         mPickOverlay.setOnClickListener(mPickListener);
118         mPick.setOnClickListener(mPickListener);
119 
120         mCancel = (Button) mContainer.findViewById(android.R.id.button2);
121         mCancel.setOnClickListener(mCancelListener);
122 
123         updateView();
124         return mContainer;
125     }
126 
127     @Override
onActivityCreated(Bundle savedInstanceState)128     public void onActivityCreated(Bundle savedInstanceState) {
129         super.onActivityCreated(savedInstanceState);
130         if (savedInstanceState != null) {
131             // Restore status
132             mAction = savedInstanceState.getInt(ACTION_KEY);
133             mCopyOperationSubType =
134                     savedInstanceState.getInt(COPY_OPERATION_SUBTYPE_KEY);
135             mPickTarget = savedInstanceState.getParcelable(PICK_TARGET_KEY);
136             mRestrictScopeStorage = savedInstanceState.getBoolean(RESTRICT_SCOPE_STORAGE_KEY);
137             updateView();
138         }
139 
140         mInjector = ((PickActivity) getActivity()).getInjector();
141     }
142 
143     @Override
onSaveInstanceState(final Bundle outState)144     public void onSaveInstanceState(final Bundle outState) {
145         super.onSaveInstanceState(outState);
146         outState.putInt(ACTION_KEY, mAction);
147         outState.putInt(COPY_OPERATION_SUBTYPE_KEY, mCopyOperationSubType);
148         outState.putParcelable(PICK_TARGET_KEY, mPickTarget);
149         outState.putBoolean(RESTRICT_SCOPE_STORAGE_KEY, mRestrictScopeStorage);
150     }
151 
152     /**
153      * @param action Which action defined in State is the picker shown for.
154      */
setPickTarget(int action, @OpType int copyOperationSubType, boolean restrictScopeStorage, DocumentInfo pickTarget)155     public void setPickTarget(int action, @OpType int copyOperationSubType,
156             boolean restrictScopeStorage, DocumentInfo pickTarget) {
157         assert(copyOperationSubType != OPERATION_DELETE);
158 
159         mAction = action;
160         mCopyOperationSubType = copyOperationSubType;
161         mRestrictScopeStorage = restrictScopeStorage;
162         mPickTarget = pickTarget;
163         if (mContainer != null) {
164             updateView();
165         }
166     }
167 
168     /**
169      * Applies the state of fragment to the view components.
170      */
updateView()171     private void updateView() {
172         if (mPickTarget != null && (
173                 mAction == State.ACTION_OPEN_TREE ||
174                         mPickTarget.isCreateSupported())) {
175             mContainer.setVisibility(View.VISIBLE);
176         } else {
177             mContainer.setVisibility(View.GONE);
178             return;
179         }
180 
181         switch (mAction) {
182             case State.ACTION_OPEN_TREE:
183                 mPick.setText(getString(R.string.open_tree_button));
184                 // When use_material3 flag is enabled, all form factors should have the pick button
185                 // wrap the text content instead of taking up the full width.
186                 if (!isUseMaterial3FlagEnabled()) {
187                     mCancel.setVisibility(View.GONE);
188                     mPick.setWidth(Integer.MAX_VALUE);
189                     mPickOverlay.setVisibility(
190                             mPickTarget.isBlockedFromTree() && mRestrictScopeStorage
191                                     ? View.VISIBLE
192                                     : View.GONE);
193                 } else if (!getActivity()
194                         .getPackageManager()
195                         .hasSystemFeature(PackageManager.FEATURE_PC)) {
196                     // On non-desktop devices the back gesture is used to cancel the picker, so
197                     // don't show the "Cancel" button on these devices and instead enable the pick
198                     // overlay which enables showing a toast when the disabled button is pressed.
199                     mCancel.setVisibility(View.GONE);
200                     mPickOverlay.setVisibility(
201                             mPickTarget.isBlockedFromTree() && mRestrictScopeStorage
202                                     ? View.VISIBLE
203                                     : View.GONE);
204                 }
205                 mPick.setEnabled(!(mPickTarget.isBlockedFromTree() && mRestrictScopeStorage));
206                 break;
207             case State.ACTION_PICK_COPY_DESTINATION:
208                 int titleId;
209                 switch (mCopyOperationSubType) {
210                     case OPERATION_COPY:
211                         titleId = R.string.button_copy;
212                         break;
213                     case OPERATION_COMPRESS:
214                         titleId = R.string.button_compress;
215                         break;
216                     case OPERATION_EXTRACT:
217                         titleId = R.string.button_extract;
218                         break;
219                     case OPERATION_MOVE:
220                         titleId = R.string.button_move;
221                         break;
222                     default:
223                         throw new UnsupportedOperationException();
224                 }
225                 mPick.setText(titleId);
226                 mCancel.setVisibility(View.VISIBLE);
227                 break;
228             default:
229                 mContainer.setVisibility(View.GONE);
230                 return;
231         }
232     }
233 }
234