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