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.
5 package org.xwalk.core.extension.api.device_capabilities;
7 import android.content.BroadcastReceiver;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.IntentFilter;
11 import android.os.Build.VERSION;
12 import android.os.Build.VERSION_CODES;
13 import android.os.Environment;
14 import android.os.StatFs;
15 import android.util.Log;
16 import android.util.SparseArray;
20 import org.json.JSONArray;
21 import org.json.JSONException;
22 import org.json.JSONObject;
23 import org.xwalk.core.extension.XWalkExtensionContext;
25 class DeviceCapabilitiesStorage {
26 private static final String TAG = "DeviceCapabilitiesStorage";
28 private DeviceCapabilities mDeviceCapabilities;
29 private XWalkExtensionContext mExtensionContext;
31 private static int mStorageCount = 0;
32 // Holds all available storages.
33 private final SparseArray<StorageUnit> mStorageList = new SparseArray<StorageUnit>();
35 private boolean mIsListening = false;
36 private IntentFilter mIntentFilter = new IntentFilter();
42 private long mCapacity;
43 private long mAvailCapacity;
46 public StorageUnit(int id, String name, String type) {
55 public int getId() { return mId; }
56 public String getName() { return mName; }
57 public String getType() { return mType; }
58 public String getPath() { return mPath; }
59 public long getCapacity() { return mCapacity; }
60 public long getAvailCapacity() { return mAvailCapacity; }
62 public void setType(String type) { mType = type;}
63 public void setPath(String path) {
68 public boolean isSame(StorageUnit unit) {
69 return mPath == unit.getPath();
72 public boolean isValid() {
73 if (mPath == null || mPath.isEmpty()) {
79 File file = new File(mPath);
80 return file.canRead();
83 @SuppressWarnings("deprecation")
84 public void updateCapacity() {
89 StatFs stat = new StatFs(mPath);
90 // FIXME(halton): After API level 18, use getTotalBytes() and
91 // getAvailableBytes() instead
93 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
94 blockSize = stat.getBlockSizeLong();
95 mCapacity = blockSize * stat.getBlockCountLong();
96 mAvailCapacity = blockSize * stat.getAvailableBlocksLong();
98 blockSize = stat.getBlockSize();
99 mCapacity = blockSize * stat.getBlockCount();
100 mAvailCapacity = blockSize * stat.getAvailableBlocks();
104 public JSONObject convertToJSON() {
105 JSONObject out = new JSONObject();
108 out.put("id", mId + 1); // Display from 1
109 out.put("name", mName);
110 out.put("type", mType);
111 out.put("capacity", mCapacity);
112 out.put("availCapacity", mAvailCapacity);
113 } catch (JSONException e) {
114 return mDeviceCapabilities.setErrorMessage(e.toString());
121 private final BroadcastReceiver mStorageListener = new BroadcastReceiver() {
123 public void onReceive(Context context, Intent intent) {
124 String action = intent.getAction();
125 if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
126 notifyAndSaveAttachedStorage();
129 if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)
130 || Intent.ACTION_MEDIA_REMOVED.equals(action)
131 || Intent.ACTION_MEDIA_BAD_REMOVAL.equals(action)) {
132 notifyAndRemoveDetachedStorage();
137 public DeviceCapabilitiesStorage(DeviceCapabilities instance,
138 XWalkExtensionContext context) {
139 mDeviceCapabilities = instance;
140 mExtensionContext = context;
142 registerIntentFilter();
144 // Fetch the original storage list
148 public JSONObject getInfo() {
149 JSONObject out = new JSONObject();
150 JSONArray arr = new JSONArray();
152 for(int i = 0; i < mStorageList.size(); i++) {
153 arr.put(mStorageList.valueAt(i).convertToJSON());
155 out.put("storages", arr);
156 } catch (JSONException e) {
157 return mDeviceCapabilities.setErrorMessage(e.toString());
163 private void initStorageList() {
164 mStorageList.clear();
167 StorageUnit unit = new StorageUnit(mStorageCount, "Internal", "fixed");
168 unit.setPath(Environment.getRootDirectory().getAbsolutePath());
169 mStorageList.put(mStorageCount, unit);
172 // Attempt to add emulated stroage first
173 int sdcardNum = mStorageCount - 1; // sdcard count from 0
174 unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "fixed");
175 if (Environment.isExternalStorageRemovable()) {
176 unit.setType("removable");
178 unit.setPath(Environment.getExternalStorageDirectory().getAbsolutePath());
180 if (unit.isValid()) {
181 mStorageList.put(mStorageCount, unit);
185 // Then attempt to add real removable storage
186 attemptAddExternalStorage();
189 private void registerIntentFilter() {
190 mIntentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
191 mIntentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
192 mIntentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
193 mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
194 mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
195 mIntentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
196 mIntentFilter.addDataScheme("file");
199 private boolean attemptAddExternalStorage() {
200 int sdcardNum = mStorageCount - 1;
201 StorageUnit unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "removable");
202 unit.setPath("/storage/sdcard" + Integer.toString(sdcardNum));
204 if (!unit.isValid()) {
208 for(int i = 0; i < mStorageList.size(); i++) {
209 if (unit.isSame(mStorageList.valueAt(i))) {
214 mStorageList.put(mStorageCount, unit);
219 public void registerListener() {
225 mExtensionContext.getActivity().registerReceiver(mStorageListener, mIntentFilter);
228 public void unregisterListener() {
233 mIsListening = false;
234 mExtensionContext.getActivity().unregisterReceiver(mStorageListener);
237 private void notifyAndSaveAttachedStorage() {
238 if(!attemptAddExternalStorage()) {
242 StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
243 JSONObject out = new JSONObject();
245 out.put("reply", "attachStorage");
246 out.put("eventName", "storageattach");
247 out.put("data", unit.convertToJSON());
249 mDeviceCapabilities.broadcastMessage(out.toString());
250 } catch (JSONException e) {
251 mDeviceCapabilities.printErrorMessage(e);
256 private void notifyAndRemoveDetachedStorage() {
257 StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
259 if(unit.getType() != "removable") {
263 JSONObject out = new JSONObject();
265 out.put("reply", "detachStorage");
266 out.put("eventName", "storagedetach");
267 out.put("data", unit.convertToJSON());
269 mDeviceCapabilities.broadcastMessage(out.toString());
270 mStorageList.remove(unit.getId());
272 } catch (JSONException e) {
273 mDeviceCapabilities.printErrorMessage(e);
277 public void onResume() {
278 // Fistly, check the lasted external storage is valid.
279 // If not, remove it and send "ondetached" event.
280 StorageUnit lastUnit = mStorageList.valueAt(mStorageList.size() - 1);
281 if(!lastUnit.isValid()) {
282 notifyAndRemoveDetachedStorage();
285 // Secondly, attmpt to add a possible external storage and send "onattached" event.
286 notifyAndSaveAttachedStorage();
291 public void onPause() {
292 unregisterListener();
295 public void onDestroy() {