1 package org.robolectric.shadows; 2 3 import android.app.DownloadManager; 4 import android.database.Cursor; 5 import android.net.Uri; 6 import android.util.Pair; 7 import java.util.ArrayList; 8 import java.util.List; 9 import java.util.Map; 10 import java.util.TreeMap; 11 import org.robolectric.Shadows; 12 import org.robolectric.annotation.Implementation; 13 import org.robolectric.annotation.Implements; 14 import org.robolectric.annotation.RealObject; 15 import org.robolectric.fakes.BaseCursor; 16 import org.robolectric.util.ReflectionHelpers; 17 18 @Implements(DownloadManager.class) 19 public class ShadowDownloadManager { 20 21 private long queueCounter = -1; // First request starts at 0 just like in the real DownloadManager 22 private Map<Long, DownloadManager.Request> requestMap = new TreeMap<>(); 23 24 @Implementation 25 public long enqueue(DownloadManager.Request request) { 26 queueCounter++; 27 requestMap.put(queueCounter, request); 28 return queueCounter; 29 } 30 31 @Implementation 32 public int remove(long... ids) { 33 int removeCount = 0; 34 for (long id : ids) { 35 if (requestMap.remove(id) != null) { 36 removeCount++; 37 } 38 } 39 return removeCount; 40 } 41 42 @Implementation 43 public Cursor query(DownloadManager.Query query) { 44 ResultCursor result = new ResultCursor(); 45 ShadowQuery shadow = Shadows.shadowOf(query); 46 long[] ids = shadow.getIds(); 47 48 if (ids != null) { 49 for (long id : ids) { 50 DownloadManager.Request request = requestMap.get(id); 51 if (request != null) { 52 result.requests.add(request); 53 } 54 } 55 } else { 56 result.requests.addAll(requestMap.values()); 57 } 58 return result; 59 } 60 61 public DownloadManager.Request getRequest(long id) { 62 return requestMap.get(id); 63 } 64 65 public int getRequestCount() { 66 return requestMap.size(); 67 } 68 69 @Implements(DownloadManager.Request.class) 70 public static class ShadowRequest { 71 @RealObject DownloadManager.Request realObject; 72 73 private int status; 74 75 public int getStatus() { 76 return this.status; 77 } 78 79 public void setStatus(int status) { 80 this.status = status; 81 } 82 83 public Uri getUri() { 84 return getFieldReflectively("mUri", realObject, Uri.class); 85 } 86 87 public Uri getDestination() { 88 return getFieldReflectively("mDestinationUri", realObject, Uri.class); 89 } 90 91 public CharSequence getTitle() { 92 return getFieldReflectively("mTitle", realObject, CharSequence.class); 93 } 94 95 public CharSequence getDescription() { 96 return getFieldReflectively("mDescription", realObject, CharSequence.class); 97 } 98 99 public CharSequence getMimeType() { 100 return getFieldReflectively("mMimeType", realObject, CharSequence.class); 101 } 102 103 public int getNotificationVisibility() { 104 return getFieldReflectively("mNotificationVisibility", realObject, Integer.class); 105 } 106 107 public int getAllowedNetworkTypes() { 108 return getFieldReflectively("mAllowedNetworkTypes", realObject, Integer.class); 109 } 110 111 public boolean getAllowedOverRoaming() { 112 return getFieldReflectively("mRoamingAllowed", realObject, Boolean.class); 113 } 114 115 public boolean getAllowedOverMetered() { 116 return getFieldReflectively("mMeteredAllowed", realObject, Boolean.class); 117 } 118 119 public boolean getVisibleInDownloadsUi() { 120 return getFieldReflectively("mIsVisibleInDownloadsUi", realObject, Boolean.class); 121 } 122 123 public List<Pair<String, String>> getRequestHeaders() { 124 return getFieldReflectively("mRequestHeaders", realObject, List.class); 125 } 126 } 127 128 @Implements(DownloadManager.Query.class) 129 public static class ShadowQuery { 130 @RealObject DownloadManager.Query realObject; 131 132 public long[] getIds() { 133 return getFieldReflectively("mIds", realObject, long[].class); 134 } 135 } 136 137 private static class ResultCursor extends BaseCursor { 138 private static final int COLUMN_INDEX_LOCAL_FILENAME = 0; 139 private static final int COLUMN_INDEX_DESCRIPTION = 1; 140 private static final int COLUMN_INDEX_REASON = 2; 141 private static final int COLUMN_INDEX_STATUS = 3; 142 private static final int COLUMN_INDEX_URI = 4; 143 private static final int COLUMN_INDEX_LOCAL_URI = 5; 144 private static final int COLUMN_INDEX_TITLE = 6; 145 146 public List<DownloadManager.Request> requests = new ArrayList<>(); 147 private int positionIndex = -1; 148 private boolean closed; 149 150 @Override 151 public int getCount() { 152 checkClosed(); 153 return requests.size(); 154 } 155 156 @Override 157 public int getPosition() { 158 return positionIndex; 159 } 160 161 @Override 162 public boolean moveToFirst() { 163 checkClosed(); 164 positionIndex = 0; 165 return !requests.isEmpty(); 166 } 167 168 @Override 169 public boolean moveToNext() { 170 checkClosed(); 171 positionIndex += 1; 172 return positionIndex < requests.size(); 173 } 174 175 @Override 176 public int getColumnIndex(String columnName) { 177 checkClosed(); 178 179 if (DownloadManager.COLUMN_LOCAL_FILENAME.equals(columnName)) { 180 return COLUMN_INDEX_LOCAL_FILENAME; 181 182 } else if (DownloadManager.COLUMN_DESCRIPTION.equals(columnName)) { 183 return COLUMN_INDEX_DESCRIPTION; 184 185 } else if (DownloadManager.COLUMN_REASON.equals(columnName)) { 186 return COLUMN_INDEX_REASON; 187 188 } else if (DownloadManager.COLUMN_STATUS.equals(columnName)) { 189 return COLUMN_INDEX_STATUS; 190 191 } else if (DownloadManager.COLUMN_URI.equals(columnName)) { 192 return COLUMN_INDEX_URI; 193 194 } else if (DownloadManager.COLUMN_LOCAL_URI.equals(columnName)) { 195 return COLUMN_INDEX_LOCAL_URI; 196 197 } else if (DownloadManager.COLUMN_TITLE.equals(columnName)) { 198 return COLUMN_INDEX_TITLE; 199 } 200 201 return -1; 202 } 203 204 @Override 205 public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { 206 checkClosed(); 207 208 int columnIndex = getColumnIndex(columnName); 209 if (columnIndex == -1) { 210 throw new IllegalArgumentException("Column not found."); 211 } 212 return columnIndex; 213 } 214 215 @Override 216 public void close() { 217 this.closed = true; 218 } 219 220 @Override 221 public boolean isClosed() { 222 return closed; 223 } 224 225 @Override 226 public String getString(int columnIndex) { 227 checkClosed(); 228 ShadowRequest request = Shadows.shadowOf(requests.get(positionIndex)); 229 switch (columnIndex) { 230 case COLUMN_INDEX_LOCAL_FILENAME: 231 return "local file name not implemented"; 232 233 case COLUMN_INDEX_REASON: 234 return "reason not implemented"; 235 236 case COLUMN_INDEX_DESCRIPTION: 237 return request.getDescription().toString(); 238 239 case COLUMN_INDEX_URI: 240 return request.getUri().toString(); 241 242 case COLUMN_INDEX_LOCAL_URI: 243 return request.getDestination().toString(); 244 245 case COLUMN_INDEX_TITLE: 246 return request.getTitle().toString(); 247 } 248 249 return "Unknown ColumnIndex " + columnIndex; 250 } 251 252 @Override 253 public int getInt(int columnIndex) { 254 checkClosed(); 255 ShadowRequest request = Shadows.shadowOf(requests.get(positionIndex)); 256 if (columnIndex == COLUMN_INDEX_STATUS) { 257 return request.getStatus(); 258 } 259 return 0; 260 } 261 262 private void checkClosed() { 263 if (closed) { 264 throw new IllegalStateException("Cursor is already closed."); 265 } 266 } 267 } 268 269 private static <T> T getFieldReflectively(String fieldName, Object object, Class<T> clazz) { 270 return clazz.cast(ReflectionHelpers.getField(object, fieldName)); 271 } 272 } 273