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