1 /*
2  * Copyright (C) 2015 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.example.android.autobackupsample;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.text.TextUtils;
23 import android.util.Log;
24 import android.view.Gravity;
25 import android.view.View;
26 import android.widget.AdapterView;
27 import android.widget.ArrayAdapter;
28 import android.widget.EditText;
29 import android.widget.Spinner;
30 import android.widget.Toast;
31 
32 import com.example.android.autobackupsample.MainActivityFragment;
33 
34 import java.io.BufferedOutputStream;
35 import java.io.File;
36 import java.io.FileOutputStream;
37 
38 /**
39  * The purpose of AddFileActivity activity is to create a data file based on the
40  * file name and size parameters specified as an Intent external parameters or with the
41  * activity UI.
42  * <p/>
43  * The optional intent parameters are
44  * {@link com.example.android.autobackupsample.AddFileActivity#FILE_NAME} and
45  * {@link com.example.android.autobackupsample.AddFileActivity#FILE_SIZE_IN_BYTES}.
46  * {@link com.example.android.autobackupsample.AddFileActivity#FILE_STORAGE}.
47  * <p/>
48  * The activity will return an
49  * {@link com.example.android.autobackupsample.MainActivityFragment#ADD_FILE_RESULT_ERROR}
50  * if intent parameters are specified incorrectly or it will display Toast messages to the user
51  * if those parameters are specified via the activity UI.
52  */
53 public class AddFileActivity extends Activity {
54 
55     private static final String TAG = "AutoBackupSample";
56 
57     /**
58      * The intent parameter that specifies a file name. The file name must be unique for the
59      * application internal directory.
60      */
61     public static final String FILE_NAME = "file_name";
62 
63     /**
64      * The intent parameter that specifies a file size in bytes. The size must be a number
65      * larger or equal to 0.
66      */
67     public static final String FILE_SIZE_IN_BYTES = "file_size_in_bytes";
68 
69     /**
70      * The file storage is an optional parameter. It should be one of these:
71      * "INTERNAL", "EXTERNAL", "DONOTBACKUP". The default option is "INTERNAL".
72      */
73     public static final String FILE_STORAGE = "file_storage";
74 
75     /**
76      * A file size multiplier. It is used to calculate the total number of bytes to be added
77      * to the file.
78      */
79     private int mSizeMultiplier = 1;
80 
81     /**
82      * Defines File Storage options.
83      */
84     private static enum FileStorage {
85         INTERNAL,
86         EXTERNAL,
87         DONOTBACKUP;
88     }
89 
90     /**
91      * Contains a selected by a user file storage option.
92      */
93     private FileStorage mFileStorage = FileStorage.INTERNAL;
94 
95     @Override
onCreate(Bundle savedInstanceState)96     public void onCreate(Bundle savedInstanceState) {
97         super.onCreate(savedInstanceState);
98         setContentView(R.layout.add_file);
99         initFileSizeSpinner();
100         initFileStorageSpinner();
101     }
102 
103     @Override
onResume()104     protected void onResume() {
105         super.onResume();
106         // If an intent has extra parameters, create the file and finish the activity.
107         if (getIntent().hasExtra(FILE_NAME) && getIntent().hasExtra(FILE_SIZE_IN_BYTES)) {
108             String fileName = getIntent().getStringExtra(FILE_NAME);
109             String sizeInBytesParamValue = getIntent().getStringExtra(FILE_SIZE_IN_BYTES);
110             String fileStorageParamValue = FileStorage.INTERNAL.toString();
111 
112             if (getIntent().hasExtra(FILE_STORAGE)) {
113                 fileStorageParamValue = getIntent().getStringExtra(FILE_STORAGE);
114             }
115 
116             if (TextUtils.isEmpty(fileName) ||
117                     isFileExists(fileName) ||
118                     !isSizeValid(sizeInBytesParamValue) ||
119                     !isFileStorageParamValid(fileStorageParamValue)) {
120                 setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
121                 finish();
122                 return;
123             }
124 
125             mFileStorage = FileStorage.valueOf(fileStorageParamValue);
126 
127             if (mFileStorage == FileStorage.EXTERNAL && !Utils.isExternalStorageAvailable()) {
128                 setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
129                 finish();
130                 return;
131             }
132 
133             createFileWithRandomDataAndFinishActivity(fileName, mFileStorage,
134                     sizeInBytesParamValue);
135         }
136     }
137 
138     /**
139      * A handler function for a Create File button click event.
140      *
141      * @param view a reference to the Create File button view.
142      */
onCreateFileButtonClick(View view)143     public void onCreateFileButtonClick(View view) {
144         EditText fileNameEditText = (EditText) findViewById(R.id.file_name);
145         EditText fileSizeEditText = (EditText) findViewById(R.id.file_size);
146         String fileName = fileNameEditText.getText().toString();
147         String fileSizeEditTextValue = fileSizeEditText.getText().toString();
148 
149         if (TextUtils.isEmpty(fileName) || isFileExists(fileName)) {
150             Toast toast = Toast.makeText(this, getText(R.string.file_exists), Toast.LENGTH_LONG);
151             toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
152             toast.show();
153             return;
154         }
155 
156         if (!isSizeValid(fileSizeEditTextValue)) {
157             Toast toast = Toast.makeText(this, getText(R.string.file_size_is_invalid),
158                     Toast.LENGTH_LONG);
159             toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
160             toast.show();
161             return;
162         }
163 
164         long fileSize = Integer.parseInt(fileSizeEditTextValue) * mSizeMultiplier;
165 
166         if (mFileStorage == FileStorage.EXTERNAL && !Utils.isExternalStorageAvailable()) {
167             Toast toast = Toast.makeText(this,
168                     getText(R.string.external_storage_unavailable), Toast.LENGTH_LONG);
169             toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
170             toast.show();
171             return;
172         }
173 
174         createFileWithRandomDataAndFinishActivity(fileName, mFileStorage, String.valueOf(fileSize));
175     }
176 
initFileSizeSpinner()177     private void initFileSizeSpinner() {
178         Spinner spinner = (Spinner) findViewById(R.id.file_size_spinner);
179         final ArrayAdapter<CharSequence> adapter =
180                 ArrayAdapter.createFromResource(this, R.array.file_size_array,
181                         android.R.layout.simple_spinner_item);
182         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
183         spinner.setAdapter(adapter);
184         spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
185             @Override
186             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
187                 String sizeMeasure = adapter.getItem(position).toString();
188                 mSizeMultiplier = (int) Math.pow(1024, position);
189                 if (Log.isLoggable(TAG, Log.DEBUG)) {
190                     Log.d(TAG, String.format("Selected: %s, %d", sizeMeasure,
191                             mSizeMultiplier));
192                 }
193             }
194 
195             @Override
196             public void onNothingSelected(AdapterView<?> parent) {
197 
198             }
199         });
200     }
201 
initFileStorageSpinner()202     private void initFileStorageSpinner() {
203         Spinner spinner = (Spinner) findViewById(R.id.storage_spinner);
204         final ArrayAdapter<CharSequence> adapter =
205                 ArrayAdapter.createFromResource(this, R.array.file_storage_array,
206                         android.R.layout.simple_spinner_item);
207         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
208         spinner.setAdapter(adapter);
209         spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
210             @Override
211             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
212                 mFileStorage = FileStorage.values()[position];
213             }
214 
215             @Override
216             public void onNothingSelected(AdapterView<?> parent) {
217 
218             }
219         });
220     }
221 
createFileWithRandomDataAndFinishActivity(String fileName, FileStorage storage, String sizeInBytes)222     private void createFileWithRandomDataAndFinishActivity(String fileName, FileStorage storage,
223                                                            String sizeInBytes) {
224         long size = Long.valueOf(sizeInBytes);
225         File file = null;
226         FileOutputStream out = null;
227         BufferedOutputStream bufOut = null;
228         try {
229             switch (storage) {
230                 case INTERNAL:
231                     file = getInternalFile(fileName);
232                     out = openFileOutput(file.getName(), Context.MODE_PRIVATE);
233                     break;
234                 case EXTERNAL:
235                     assert Utils.isExternalStorageAvailable() :
236                             "The external storage is not available";
237                     File externalAppDir = getExternalFilesDir(null);
238                     file = new File(externalAppDir, fileName);
239                     out = new FileOutputStream(file);
240                     break;
241                 case DONOTBACKUP:
242                     file = new File(getNoBackupFilesDir(), fileName);
243                     out = new FileOutputStream(file);
244                     break;
245             }
246 
247             if (file == null || out == null) {
248                 Log.d(TAG, "Unable to create file output stream");
249                 // Returning back to the caller activity.
250                 setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
251                 finish();
252                 return;
253             }
254 
255             bufOut = new BufferedOutputStream(out);
256             for (int i = 0; i < size; i++) {
257                 byte b = (byte) (255 * Math.random());
258                 bufOut.write(b);
259             }
260 
261             String message = String.format("File created: %s, size: %s bytes",
262                     file.getAbsolutePath(), sizeInBytes);
263 
264             Toast toast = Toast.makeText(this, message, Toast.LENGTH_LONG);
265             toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
266             toast.show();
267             Log.d(TAG, message);
268 
269             // Returning back to the caller activity.
270             setResult(MainActivityFragment.ADD_FILE_RESULT_SUCCESS);
271             finish();
272         } catch (Exception e) {
273             Log.e(TAG, e.getMessage(), e);
274             // Returning back to the caller activity.
275             setResult(MainActivityFragment.ADD_FILE_RESULT_ERROR);
276             finish();
277         } finally {
278             if (bufOut != null) {
279                 try {
280                     bufOut.close();
281                 } catch (Exception e) {
282                     // Ignore.
283                 }
284             }
285         }
286     }
287 
isFileExists(String fileName)288     private boolean isFileExists(String fileName) {
289         File file = getInternalFile(fileName);
290         if (file.exists()) {
291             if (Log.isLoggable(TAG, Log.DEBUG)) {
292                 Log.d(TAG, "This file exists: " + file.getName());
293             }
294             return true;
295         }
296         return false;
297     }
298 
isSizeValid(String sizeInBytesParamValue)299     private boolean isSizeValid(String sizeInBytesParamValue) {
300         long sizeInBytes = 0;
301         try {
302             sizeInBytes = Long.valueOf(sizeInBytesParamValue);
303         } catch (NumberFormatException e) {
304             if (Log.isLoggable(TAG, Log.DEBUG)) {
305                 Log.d(TAG, "Invalid file size: " + sizeInBytesParamValue);
306             }
307             return false;
308         }
309 
310         // Validate file size value. It should be 0 or a positive number.
311         if (sizeInBytes < 0) {
312             if (Log.isLoggable(TAG, Log.DEBUG)) {
313                 Log.d(TAG, "Invalid file size: " + sizeInBytes);
314             }
315             return false;
316         }
317         return true;
318     }
319 
isFileStorageParamValid(String fileStorage)320     private boolean isFileStorageParamValid(String fileStorage) {
321         try {
322             mFileStorage = FileStorage.valueOf(fileStorage);
323         } catch (IllegalArgumentException e) {
324             if (Log.isLoggable(TAG, Log.DEBUG)) {
325                 Log.d(TAG, "Invalid file storage: " + fileStorage);
326             }
327             return false;
328         }
329         return true;
330     }
331 
getInternalFile(String fileName)332     private File getInternalFile(String fileName) {
333         File internalAppDir = getFilesDir();
334         return new File(internalAppDir, fileName);
335     }
336 
337 }