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.picker; 18 19 import android.app.Fragment; 20 import android.app.FragmentManager; 21 import android.app.FragmentTransaction; 22 import android.content.Context; 23 import android.os.Bundle; 24 import android.text.Editable; 25 import android.text.TextUtils; 26 import android.text.TextWatcher; 27 import android.view.KeyEvent; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.Button; 32 import android.widget.EditText; 33 import android.widget.ImageView; 34 import android.widget.ProgressBar; 35 import android.widget.TextView; 36 37 import com.android.documentsui.IconUtils; 38 import com.android.documentsui.Injector; 39 import com.android.documentsui.R; 40 import com.android.documentsui.base.BooleanConsumer; 41 import com.android.documentsui.base.DocumentInfo; 42 import com.android.documentsui.base.Shared; 43 44 /** 45 * Display document title editor and save button. 46 */ 47 public class SaveFragment extends Fragment { 48 public static final String TAG = "SaveFragment"; 49 50 private final BooleanConsumer mInProgressStateListener = this::setPending; 51 52 private Injector<ActionHandler<PickActivity>> mInjector; 53 private DocumentInfo mReplaceTarget; 54 private EditText mDisplayName; 55 private TextView mSave; 56 private ProgressBar mProgress; 57 private boolean mIgnoreNextEdit; 58 59 private static final String EXTRA_MIME_TYPE = "mime_type"; 60 private static final String EXTRA_DISPLAY_NAME = "display_name"; 61 show(FragmentManager fm, String mimeType, String displayName)62 static void show(FragmentManager fm, String mimeType, String displayName) { 63 final Bundle args = new Bundle(); 64 args.putString(EXTRA_MIME_TYPE, mimeType); 65 args.putString(EXTRA_DISPLAY_NAME, displayName); 66 67 final SaveFragment fragment = new SaveFragment(); 68 fragment.setArguments(args); 69 70 final FragmentTransaction ft = fm.beginTransaction(); 71 ft.replace(R.id.container_save, fragment, TAG); 72 ft.commitAllowingStateLoss(); 73 } 74 get(FragmentManager fm)75 public static SaveFragment get(FragmentManager fm) { 76 return (SaveFragment) fm.findFragmentByTag(TAG); 77 } 78 79 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)80 public View onCreateView( 81 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 82 final Context context = inflater.getContext(); 83 84 final View view = inflater.inflate(R.layout.fragment_save, container, false); 85 86 final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); 87 icon.setImageDrawable( 88 IconUtils.loadMimeIcon(context, getArguments().getString(EXTRA_MIME_TYPE))); 89 90 mDisplayName = (EditText) view.findViewById(android.R.id.title); 91 mDisplayName.addTextChangedListener(mDisplayNameWatcher); 92 mDisplayName.setText(getArguments().getString(EXTRA_DISPLAY_NAME)); 93 mDisplayName.setOnKeyListener( 94 new View.OnKeyListener() { 95 @Override 96 public boolean onKey(View v, int keyCode, KeyEvent event) { 97 // Only handle key-down events. This is simpler, consistent with most other 98 // UIs, and enables the handling of repeated key events from holding down a 99 // key. 100 if (event.getAction() != KeyEvent.ACTION_DOWN) { 101 return false; 102 } 103 104 // Returning false in this method will bubble the event up to 105 // {@link BaseActivity#onKeyDown}. In order to prevent backspace popping 106 // documents once the textView is empty, we are going to trap it here. 107 if (keyCode == KeyEvent.KEYCODE_DEL 108 && TextUtils.isEmpty(mDisplayName.getText())) { 109 return true; 110 } 111 112 if (keyCode == KeyEvent.KEYCODE_ENTER && mSave.isEnabled()) { 113 performSave(); 114 return true; 115 } 116 return false; 117 } 118 }); 119 120 mSave = (TextView) view.findViewById(android.R.id.button1); 121 mSave.setOnClickListener(mSaveListener); 122 mSave.setEnabled(false); 123 124 mProgress = (ProgressBar) view.findViewById(android.R.id.progress); 125 126 return view; 127 } 128 129 @Override onActivityCreated(Bundle savedInstanceState)130 public void onActivityCreated(Bundle savedInstanceState) { 131 super.onActivityCreated(savedInstanceState); 132 mInjector = ((PickActivity) getActivity()).getInjector(); 133 134 if (savedInstanceState != null) { 135 mReplaceTarget = savedInstanceState.getParcelable(Shared.EXTRA_DOC); 136 } 137 } 138 139 @Override onSaveInstanceState(Bundle outBundle)140 public void onSaveInstanceState(Bundle outBundle) { 141 super.onSaveInstanceState(outBundle); 142 143 outBundle.putParcelable(Shared.EXTRA_DOC, mReplaceTarget); 144 } 145 146 private TextWatcher mDisplayNameWatcher = new TextWatcher() { 147 @Override 148 public void onTextChanged(CharSequence s, int start, int before, int count) { 149 if (mIgnoreNextEdit) { 150 mIgnoreNextEdit = false; 151 } else { 152 mReplaceTarget = null; 153 } 154 } 155 156 @Override 157 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 158 // ignored 159 } 160 161 @Override 162 public void afterTextChanged(Editable s) { 163 // ignored 164 } 165 }; 166 167 private View.OnClickListener mSaveListener = new View.OnClickListener() { 168 @Override 169 public void onClick(View v) { 170 performSave(); 171 } 172 173 }; 174 performSave()175 private void performSave() { 176 if (mReplaceTarget != null) { 177 mInjector.actions.saveDocument(getChildFragmentManager(), mReplaceTarget); 178 } else { 179 final String mimeType = getArguments().getString(EXTRA_MIME_TYPE); 180 final String displayName = mDisplayName.getText().toString(); 181 mInjector.actions.saveDocument(mimeType, displayName, mInProgressStateListener); 182 } 183 } 184 185 /** 186 * Set given document as target for in-place writing if user hits save 187 * without changing the filename. Can be set to {@code null} if user 188 * navigates outside the target directory. 189 */ setReplaceTarget(DocumentInfo replaceTarget)190 public void setReplaceTarget(DocumentInfo replaceTarget) { 191 mReplaceTarget = replaceTarget; 192 193 if (mReplaceTarget != null) { 194 getArguments().putString(EXTRA_DISPLAY_NAME, replaceTarget.displayName); 195 mIgnoreNextEdit = true; 196 mDisplayName.setText(replaceTarget.displayName); 197 } 198 } 199 prepareForDirectory(DocumentInfo cwd)200 public void prepareForDirectory(DocumentInfo cwd) { 201 setSaveEnabled(cwd != null && cwd.isCreateSupported()); 202 } 203 setSaveEnabled(boolean enabled)204 private void setSaveEnabled(boolean enabled) { 205 mSave.setEnabled(enabled); 206 } 207 setPending(boolean pending)208 private void setPending(boolean pending) { 209 mSave.setVisibility(pending ? View.INVISIBLE : View.VISIBLE); 210 mProgress.setVisibility(pending ? View.VISIBLE : View.GONE); 211 } 212 } 213