a84b4dcaa831b00f663ab1b169a0e44b74bdfa0a
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / java / src / org / xwalk / runtime / extension / api / device_capabilities / DeviceCapabilitiesStorage.java
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.runtime.extension.api.device_capabilities;
6
7 import android.content.BroadcastReceiver;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.IntentFilter;
11 import android.os.Environment;
12 import android.os.StatFs;
13 import android.util.Log;
14 import android.util.SparseArray;
15
16 import java.io.File;
17
18 import org.json.JSONArray;
19 import org.json.JSONException;
20 import org.json.JSONObject;
21 import org.xwalk.runtime.extension.XWalkExtensionContext;
22
23 class DeviceCapabilitiesStorage {
24     private static final String TAG = "DeviceCapabilitiesStorage";
25
26     private DeviceCapabilities mDeviceCapabilities;
27     private XWalkExtensionContext mExtensionContext;
28
29     private static int mStorageCount = 0;
30     // Holds all available storages.
31     private final SparseArray<StorageUnit> mStorageList = new SparseArray<StorageUnit>();
32
33     private boolean mIsListening = false;
34     private IntentFilter mIntentFilter = new IntentFilter();
35
36     class StorageUnit {
37         private int mId;
38         private String mName;
39         private String mType;
40         private long mCapacity;
41         private long mAvailCapacity;
42         private String mPath;
43
44         public StorageUnit(int id, String name, String type) {
45             mId = id;
46             mName = name;
47             mType = type;
48             mPath = "";
49             mCapacity = 0;
50             mAvailCapacity = 0;
51         }
52
53         public int getId() { return mId; }
54         public String getName() { return mName; }
55         public String getType() { return mType; }
56         public String getPath() { return mPath; }
57         public long getCapacity() { return mCapacity; }
58         public long getAvailCapacity() { return mAvailCapacity; }
59
60         public void setType(String type) { mType = type;}
61         public void setPath(String path) {
62           mPath = path;
63           updateCapacity();
64         }
65
66         public boolean isSame(StorageUnit unit) {
67             return mPath == unit.getPath();
68         }
69
70         public boolean isValid() {
71             if (mPath == null || mPath.isEmpty()) {
72                 mCapacity = 0;
73                 mAvailCapacity = 0;
74                 return false;
75             }
76
77             File file = new File(mPath);
78             return file.canRead();
79         }
80
81         public void updateCapacity() {
82             if (!isValid()) {
83                 return;
84             }
85
86             StatFs stat = new StatFs(mPath);
87             // FIXME(halton): After API level 18, use getTotalBytes() and
88             // getAvailableBytes() instead
89             long blockSize = stat.getBlockSize();
90             mCapacity = blockSize * stat.getBlockCount();
91             mAvailCapacity = blockSize * stat.getAvailableBlocks();
92         }
93
94         public JSONObject convertToJSON() {
95             JSONObject out = new JSONObject();
96
97             try {
98                 out.put("id", mId + 1); // Display from 1
99                 out.put("name", mName);
100                 out.put("type", mType);
101                 out.put("capacity", mCapacity);
102                 out.put("availCapacity", mAvailCapacity);
103             } catch (JSONException e) {
104                 return mDeviceCapabilities.setErrorMessage(e.toString());
105             }
106
107             return out;
108         }
109     }
110
111     private final BroadcastReceiver mStorageListener = new BroadcastReceiver() {
112         @Override
113         public void onReceive(Context context, Intent intent) {
114             String action = intent.getAction();
115             if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
116                 notifyAndSaveAttachedStorage();
117             }
118
119             if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)
120                 || Intent.ACTION_MEDIA_REMOVED.equals(action)
121                 || Intent.ACTION_MEDIA_BAD_REMOVAL.equals(action)) {
122                 notifyAndRemoveDetachedStorage();
123             }
124         }
125     };
126
127     public DeviceCapabilitiesStorage(DeviceCapabilities instance,
128                                      XWalkExtensionContext context) {
129         mDeviceCapabilities = instance;
130         mExtensionContext = context;
131
132         registerIntentFilter();
133
134         // Fetch the original storage list
135         initStorageList();
136     }
137
138     public JSONObject getInfo() {
139         JSONObject out = new JSONObject();
140         JSONArray arr = new JSONArray();
141         try {
142             for(int i = 0; i < mStorageList.size(); i++) {
143                 arr.put(mStorageList.valueAt(i).convertToJSON());
144             }
145             out.put("storages", arr);
146         } catch (JSONException e) {
147             return mDeviceCapabilities.setErrorMessage(e.toString());
148         }
149
150         return out;
151     }
152
153     private void initStorageList() {
154         mStorageList.clear();
155         mStorageCount = 0;
156
157         StorageUnit unit = new StorageUnit(mStorageCount, "Internal", "fixed");
158         unit.setPath(Environment.getRootDirectory().getAbsolutePath());
159         mStorageList.put(mStorageCount, unit);
160         ++mStorageCount;
161
162         // Attempt to add emulated stroage first
163         int sdcardNum = mStorageCount - 1; // sdcard count from 0
164         unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "fixed");
165         if (Environment.isExternalStorageRemovable()) {
166             unit.setType("removable");
167         }
168         unit.setPath(Environment.getExternalStorageDirectory().getAbsolutePath());
169
170         if (unit.isValid()) {
171             mStorageList.put(mStorageCount, unit);
172             ++mStorageCount;
173         }
174
175         // Then attempt to add real removable storage
176         attemptAddExternalStorage();
177     }
178
179     private void registerIntentFilter() {
180         mIntentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
181         mIntentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
182         mIntentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
183         mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
184         mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
185         mIntentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
186         mIntentFilter.addDataScheme("file");
187     }
188
189     private boolean attemptAddExternalStorage() {
190         int sdcardNum = mStorageCount - 1;
191         StorageUnit unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "removable");
192         unit.setPath("/storage/sdcard" + Integer.toString(sdcardNum));
193
194         if (!unit.isValid()) {
195             return false;
196         }
197
198         for(int i = 0; i < mStorageList.size(); i++) {
199             if (unit.isSame(mStorageList.valueAt(i))) {
200                 return false;
201             }
202         }
203
204         mStorageList.put(mStorageCount, unit);
205         ++mStorageCount;
206         return true;
207     }
208
209     public void registerListener() {
210         if (mIsListening) {
211             return;
212         }
213
214         mIsListening = true;
215         mExtensionContext.getActivity().registerReceiver(mStorageListener, mIntentFilter);
216     }
217
218     public void unregisterListener() {
219         if (!mIsListening) {
220             return;
221         }
222
223         mIsListening = false;
224         mExtensionContext.getActivity().unregisterReceiver(mStorageListener);
225     }
226
227     private void notifyAndSaveAttachedStorage() {
228         if(!attemptAddExternalStorage()) {
229             return;
230         }
231
232         StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
233         JSONObject out = new JSONObject();
234         try {
235             out.put("reply", "attachStorage");
236             out.put("eventName", "storageattach");
237             out.put("data", unit.convertToJSON());
238
239             mDeviceCapabilities.broadcastMessage(out.toString());
240         } catch (JSONException e) {
241             mDeviceCapabilities.printErrorMessage(e);
242         }
243
244     }
245
246     private void notifyAndRemoveDetachedStorage() {
247         StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
248
249         if(unit.getType() != "removable") {
250             return;
251         }
252
253         JSONObject out = new JSONObject();
254         try {
255             out.put("reply", "detachStorage");
256             out.put("eventName", "storagedetach");
257             out.put("data", unit.convertToJSON());
258
259             mDeviceCapabilities.broadcastMessage(out.toString());
260             mStorageList.remove(unit.getId());
261             --mStorageCount;
262         } catch (JSONException e) {
263             mDeviceCapabilities.printErrorMessage(e);
264         }
265     }
266
267     public void onResume() {
268         // Fistly, check the lasted external storage is valid.
269         // If not, remove it and send "ondetached" event.
270         StorageUnit lastUnit = mStorageList.valueAt(mStorageList.size() - 1);
271         if(!lastUnit.isValid()) {
272             notifyAndRemoveDetachedStorage();
273         }
274
275         // Secondly, attmpt to add a possible external storage and send "onattached" event.
276         notifyAndSaveAttachedStorage();
277
278         registerListener();
279     }
280
281     public void onPause() {
282         unregisterListener();
283     }
284
285     public void onDestroy() {
286     }
287 }