1 /*
2  * Copyright (C) 2016 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.functional.downloadapp;
18 
19 import android.app.AlarmManager;
20 import android.app.DownloadManager;
21 import android.app.DownloadManager.Query;
22 import android.app.DownloadManager.Request;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.database.Cursor;
26 import android.icu.util.Calendar;
27 import android.net.Uri;
28 import android.os.Environment;
29 import android.support.test.launcherhelper.ILauncherStrategy;
30 import android.support.test.launcherhelper.LauncherStrategyFactory;
31 import android.support.test.uiautomator.By;
32 import android.support.test.uiautomator.UiDevice;
33 import android.support.test.uiautomator.UiObject2;
34 import android.support.test.uiautomator.Until;
35 import android.util.Log;
36 
37 import junit.framework.Assert;
38 
39 import java.util.ArrayList;
40 import java.util.Hashtable;
41 import java.util.List;
42 import java.util.Random;
43 
44 public class DownloadAppTestHelper {
45     private static DownloadAppTestHelper mInstance = null;
46     public static final String[] FILE_TYPES = new String[] {
47             "pdf", "jpg", "jpeg", "doc", "xls", "txt", "rtf", "ppt", "gif", "png"
48     };
49 
50     public static final String PACKAGE_NAME = "com.android.documentsui";
51     public static final String APP_NAME = "Downloads";
52     public static final String TEST_TAG = "DownloadAppTest";
53     public final int TIMEOUT = 500;
54     public final int MIN_FILENAME_LEN = 4;
55     private Context mContext = null;
56     private UiDevice mDevice = null;
57     private DownloadManager mDownloadMgr;
58     private Hashtable<String, DlObjSizeTimePair> mDownloadedItems =
59             new Hashtable<String, DlObjSizeTimePair>();
60     public ILauncherStrategy mLauncherStrategy;
61 
DownloadAppTestHelper(UiDevice device, Context context)62     private DownloadAppTestHelper(UiDevice device, Context context) {
63         mDevice = device;
64         mContext = context;
65         mLauncherStrategy = LauncherStrategyFactory.getInstance(mDevice).getLauncherStrategy();
66     }
67 
getInstance(UiDevice device, Context context)68     public static DownloadAppTestHelper getInstance(UiDevice device, Context context) {
69         if (mInstance == null) {
70             mInstance = new DownloadAppTestHelper(device, context);
71         }
72         return mInstance;
73     }
74 
getDLManager()75     public DownloadManager getDLManager() {
76         return (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
77     }
78 
launchApp(String packageName, String appName)79     public void launchApp(String packageName, String appName) {
80         if (!mDevice.hasObject(By.pkg(packageName).depth(0))) {
81             mLauncherStrategy.launch(appName, packageName);
82         }
83     }
84 
85     /** sort items in Download app by name, size, time */
sortByParam(String sortby)86     public void sortByParam(String sortby) {
87         UiObject2 sortMenu = mDevice
88                 .wait(Until.findObject(By.res(PACKAGE_NAME, "menu_sort")), 200);
89         if (sortMenu == null) {
90             mDevice.wait(Until.findObject(By.desc("More options")), 200).click();
91             sortMenu = mDevice.wait(Until.findObject(By.res("android:id/submenuarrow")), 200);
92         }
93         sortMenu.click();
94         mDevice.wait(Until.findObject(By.text(String.format("By %s", sortby))), 200).click();
95         mDevice.waitForIdle();
96     }
97 
98     /** returns text list of items in Download app */
getDownloadItemNames()99     public List<String> getDownloadItemNames() {
100         List<UiObject2> itmesList = mDevice.wait(Until.findObjects(By.res("android:id/title")),
101                 TIMEOUT);
102         List<String> nameList = new ArrayList<String>();
103         for (UiObject2 item : itmesList) {
104             nameList.add(item.getText());
105         }
106         return nameList;
107     }
108 
109     /** verifies items in DownloadApp UI are sorted by name */
verifySortedByName()110     public Boolean verifySortedByName() {
111         List<String> nameList = getDownloadItemNames();
112         for (int i = 0; i < (nameList.size() - 1); ++i) {
113             if (nameList.get(i).compareToIgnoreCase(nameList.get(i + 1)) > 0) {
114                 return false;
115             }
116         }
117         return true;
118     }
119 
120     /** verifies items in DownloadApp UI are sorted by size */
verifySortedBySize()121     public Boolean verifySortedBySize() {
122         List<String> nameList = getDownloadItemNames();
123         for (int i = 0; i < (nameList.size() - 1); ++i) {
124             DlObjSizeTimePair firstItem = mDownloadedItems.get(nameList.get(i));
125             DlObjSizeTimePair secondItem = mDownloadedItems.get(nameList.get(i + 1));
126             if (firstItem != null && secondItem != null
127                     && firstItem.dlObjSize < secondItem.dlObjSize) {
128                 return false;
129             }
130         }
131         return true;
132     }
133 
134     /** verifies items in DownloadApp UI are sorted by time */
verifySortedByTime()135     public Boolean verifySortedByTime() {
136         List<String> nameList = getDownloadItemNames();
137         for (int i = 0; i < (nameList.size() - 1); ++i) {
138             DlObjSizeTimePair firstItem = mDownloadedItems.get(nameList.get(i));
139             DlObjSizeTimePair secondItem = mDownloadedItems.get(nameList.get(i + 1));
140             if (firstItem != null && secondItem != null
141                     && firstItem.dlObjTimeInMilliSec < secondItem.dlObjTimeInMilliSec) {
142                 return false;
143             }
144         }
145         return true;
146     }
147 
verifyDownloadViewType(UIViewType view)148     public void verifyDownloadViewType(UIViewType view) {
149         int counter = 5;
150         UiObject2 viewTypeObj = null;
151         while ((viewTypeObj = mDevice.wait(
152                 Until.findObject(By.res(String.format("%s:id/%s",
153                         DownloadAppTestHelper.PACKAGE_NAME,view.toString()))),200)) == null
154                         && counter-- > 0);
155         Assert.assertNotNull(viewTypeObj);
156     }
157 
158     /**
159      * Create word of random assortment of lower/upper case letters
160      */
161     /** set system time to random n[0..29] days earlier */
changeSystemTime(long timeToSet)162     public long changeSystemTime(long timeToSet) {
163         Calendar c = Calendar.getInstance();
164         c.setTimeInMillis(timeToSet);
165         AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
166         am.setTime(c.getTimeInMillis());
167         return c.getTimeInMillis();
168     }
169 
170     /** add some content to download DB using DownloadManager.addCompletedDownload api */
populateContentInDLApp(int count)171     public void populateContentInDLApp(int count) {
172         int totalDownloaded = getTotalNumberDownloads();
173         if (totalDownloaded >= count) {
174             return;
175         }
176         Random random = new Random();
177         long currentTime = System.currentTimeMillis();
178         for (int i = 0; i < (count - totalDownloaded); ++i) {
179             String fileName = String.format("%s.%s",
180                     DownloadAppTestHelper.randomWord(random.nextInt(8) + MIN_FILENAME_LEN),
181                     DownloadAppTestHelper.FILE_TYPES[random.nextInt(FILE_TYPES.length)]);
182             int size = random.nextInt(1000);
183             // changing system time to simulate the usecase "downloaded items over a period of time"
184             long timeInMiliSec = changeSystemTime(
185                     System.currentTimeMillis() - random.nextInt(30 * 24) * (60 * 60 * 1000));
186             long dlId = -1;
187 
188             dlId = getDLManager().addCompletedDownload(
189                     fileName,
190                     String.format("%s Desc",
191                             DownloadAppTestHelper.randomWord(random.nextInt(8) + MIN_FILENAME_LEN)),
192                     Boolean.FALSE,
193                     DownloadAppTestHelper.FILE_TYPES[random.nextInt(FILE_TYPES.length)],
194                     Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
195                             .getAbsolutePath(),
196                     size, Boolean.FALSE);
197             Assert.assertFalse("Add to DonwloadDB has failed!", dlId == -1);
198             Log.d(TEST_TAG, String.format("Adding Name = %s, size = %d, time = %d", fileName,
199                     size, timeInMiliSec));
200             mDownloadedItems.put(fileName, new DlObjSizeTimePair(size, timeInMiliSec));
201         }
202         changeSystemTime(currentTime);
203     }
204 
205     /** add some content to download DB directly in hacky way to bypass addCompletedDownload Api*/
addToDownloadContentDB(String title, String description, boolean isMediaScannerScannable, String mimeType, String path, long length, boolean showNotification)206     public long addToDownloadContentDB(String title, String description,
207             boolean isMediaScannerScannable, String mimeType, String path, long length,
208             boolean showNotification) {
209 
210         boolean allowWrite = Boolean.FALSE;
211         Uri uri = Uri.parse("http://blah-blah"); // just put something in url format
212         Uri referer = null;
213         Request request;
214         request = new Request(uri);
215         ContentValues values = new ContentValues();
216         /**
217          * a hacky way to insert into the Download DB direct bypassing the api with minimal data
218          * Constants have been taken from
219          * /platform/frameworks/base/+/master/core/java/android/provider/Downloads.java
220          */
221         values.put("title", title);
222         values.put("description", description);
223         values.put("mimetype", mimeType);
224         values.put("is_public_api", true);
225         values.put("scanned", isMediaScannerScannable);
226         values.put("is_visible_in_downloads_ui", Boolean.TRUE);
227         values.put("destination", 6); // 6: show the download item in app
228         values.put("_data", path); // location to save the downloaded file
229         values.put("status", 200); // 200 : STATUS_SUCCESS
230         values.put("total_bytes", length);
231         values.put("visibility", (showNotification)
232                 ? Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION : Request.VISIBILITY_HIDDEN);
233         Uri downloadUri = mContext.getContentResolver()
234                 .insert(Uri.parse("content://downloads/my_downloads"), values);
235         if (downloadUri == null) {
236             return -1;
237         }
238         return Long.parseLong(downloadUri.getLastPathSegment());
239     }
240 
241     /** remove downloads from download content db */
removeContentInDLApp()242     public void removeContentInDLApp() {
243         Cursor cursor = null;
244         try {
245             Query query = new Query();
246             cursor = getDLManager().query(query);
247             int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
248             long[] removeIds = new long[cursor.getCount() - 1];
249             Log.d(TEST_TAG, String.format("Remove Size is = %d", cursor.getCount()));
250             for (int i = 0; i < (cursor.getCount() - 1); i++) {
251                 cursor.moveToNext();
252                 removeIds[i] = cursor.getLong(columnIndex);
253             }
254             if (removeIds.length > 0) {
255                 Assert.assertEquals(removeIds.length, getDLManager().remove(removeIds));
256             }
257         } finally {
258             if (cursor != null) {
259                 cursor.close();
260             }
261         }
262     }
263 
getTotalNumberDownloads()264     public int getTotalNumberDownloads() {
265         Cursor cursor = null;
266         try {
267             Query query = new Query();
268             cursor = getDLManager().query(query);
269             return cursor.getCount();
270         } finally {
271             if (cursor != null) {
272                 cursor.close();
273             }
274         }
275     }
276 
getDownloadItemCountById(long[] downloadId)277     public int getDownloadItemCountById(long[] downloadId) {
278         Cursor cursor = null;
279         int total = 0;
280         try {
281             Query query = new Query().setFilterById(downloadId);
282             cursor = getDLManager().query(query);
283             total = cursor.getCount();
284         } finally {
285             if (cursor != null) {
286                 cursor.close();
287             }
288         }
289 
290         return total;
291     }
292 
randomWord(int length)293     public static String randomWord(int length) {
294         Random random = new Random();
295         StringBuilder result = new StringBuilder();
296         for (int j = 0; j < length; j++) {
297             int base = random.nextInt(2) == 0 ? 'A' : 'a';
298             result.append((char) (random.nextInt(26) + base));
299         }
300         return result.toString();
301     }
302 
303     /**
304      * Class to hold size and time info on downloaded items
305      */
306     class DlObjSizeTimePair {
307         int dlObjSize;
308         long dlObjTimeInMilliSec;
309 
DlObjSizeTimePair(int size, long time)310         public DlObjSizeTimePair(int size, long time) {
311             this.dlObjSize = size;
312             this.dlObjTimeInMilliSec = time;
313         }
314     }
315 
316     public enum UIViewType {
317         LIST {
toString()318             public String toString() {
319                 return "menu_list";
320             }
321         },
322         GRID {
toString()323             public String toString() {
324                 return "menu_grid";
325             }
326         }
327     };
328 }
329