1package org.robolectric.shadows;
2
3import android.app.DownloadManager;
4import android.database.Cursor;
5import android.net.Uri;
6import android.util.Pair;
7import java.util.ArrayList;
8import java.util.List;
9import java.util.Map;
10import java.util.TreeMap;
11import org.robolectric.Shadows;
12import org.robolectric.annotation.Implementation;
13import org.robolectric.annotation.Implements;
14import org.robolectric.annotation.RealObject;
15import org.robolectric.fakes.BaseCursor;
16import org.robolectric.util.ReflectionHelpers;
17
18@Implements(DownloadManager.class)
19public 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