Merge lp:~karni/ubuntuone-android-music/v2-cache-settings into lp:ubuntuone-android-music/v2
- v2-cache-settings
- Merge into v2
Proposed by
Michał Karnicki
Status: | Merged |
---|---|
Merged at revision: | 62 |
Proposed branch: | lp:~karni/ubuntuone-android-music/v2-cache-settings |
Merge into: | lp:ubuntuone-android-music/v2 |
Diff against target: |
1073 lines (+857/-44) 16 files modified
main/AndroidManifest.xml (+7/-8) main/res/layout/activity_preferences.xml (+13/-0) main/res/layout/seek_bar_preference.xml (+54/-0) main/res/xml/preference_headers.xml (+31/-0) main/res/xml/preferences_account.xml (+6/-0) main/res/xml/preferences_cache.xml (+26/-0) main/src/com/ubuntuone/android/music/Constants.java (+10/-0) main/src/com/ubuntuone/android/music/UbuntuOneMusic.java (+3/-0) main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java (+19/-1) main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java (+5/-0) main/src/com/ubuntuone/android/music/ui/HomeActivity.java (+1/-1) main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java (+269/-0) main/src/com/ubuntuone/android/music/ui/SettingsActivity.java (+0/-29) main/src/com/ubuntuone/android/music/util/AccountUtils.java (+2/-5) main/src/com/ubuntuone/android/music/util/SeekBarPreference.java (+195/-0) main/src/com/ubuntuone/android/music/util/StorageUtils.java (+216/-0) |
To merge this branch: | bzr merge lp:~karni/ubuntuone-android-music/v2-cache-settings |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Diego Sarmentero (community) | Approve | ||
Review via email: mp+144973@code.launchpad.net |
Commit message
Added cache preferences.
Description of the change
Added storage utility class providing storage related info.
Added custom seek bar preference.
Added tablet ready preference activity.
Implemented cache: storage, size and cleaning preferences.
Misc tiny changes I allowed myself to add in small, separate commits.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'main/AndroidManifest.xml' |
2 | --- main/AndroidManifest.xml 2013-01-23 02:37:15 +0000 |
3 | +++ main/AndroidManifest.xml 2013-01-25 16:20:29 +0000 |
4 | @@ -1,19 +1,17 @@ |
5 | <?xml version="1.0" encoding="utf-8"?> |
6 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
7 | package="com.ubuntuone.android.music" |
8 | + android:sharedUserId="com.ubuntu.one" |
9 | + android:sharedUserLabel="@string/shared_user" |
10 | android:versionCode="1" |
11 | android:versionName="0.1.0" > |
12 | |
13 | - <!-- |
14 | - FIXME Set shared user id: |
15 | - android:sharedUserId="com.ubuntuone.android" |
16 | - android:sharedUserLabel="@string/shared_user" |
17 | - --> |
18 | <uses-sdk |
19 | android:minSdkVersion="10" |
20 | android:targetSdkVersion="14" /> |
21 | |
22 | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
23 | + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> |
24 | <uses-permission android:name="android.permission.GET_ACCOUNTS" /> |
25 | <uses-permission android:name="android.permission.INTERNET" /> |
26 | <uses-permission android:name="android.permission.READ_PHONE_STATE" /> |
27 | @@ -23,6 +21,7 @@ |
28 | |
29 | <application |
30 | android:name=".UbuntuOneMusic" |
31 | + android:allowBackup="false" |
32 | android:label="@string/app_name" |
33 | android:theme="@style/Theme.Sherlock.Light" |
34 | android:uiOptions="splitActionBarWhenNarrow" > |
35 | @@ -75,8 +74,8 @@ |
36 | android:screenOrientation="portrait" > |
37 | </activity> |
38 | <activity |
39 | - android:name=".ui.SettingsActivity" |
40 | - android:label="U1 Music Settings" |
41 | + android:name=".ui.PreferencesActivity" |
42 | + android:label="Ubuntu One Music Settings" |
43 | android:theme="@style/Theme.Sherlock.Light" > |
44 | </activity> |
45 | |
46 | @@ -95,8 +94,8 @@ |
47 | |
48 | <provider |
49 | android:name=".provider.MusicProvider" |
50 | + android:authorities="com.ubuntuone.android.music" |
51 | android:exported="false" |
52 | - android:authorities="com.ubuntuone.android.music" |
53 | android:label="@string/app_name" |
54 | android:syncable="true" |
55 | android:writePermission="com.ubuntuone.android.music.permission.WRITE_MUSIC_DATA" /> |
56 | |
57 | === added file 'main/res/layout/activity_preferences.xml' |
58 | --- main/res/layout/activity_preferences.xml 1970-01-01 00:00:00 +0000 |
59 | +++ main/res/layout/activity_preferences.xml 2013-01-25 16:20:29 +0000 |
60 | @@ -0,0 +1,13 @@ |
61 | +<?xml version="1.0" encoding="utf-8"?> |
62 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
63 | + android:layout_width="match_parent" |
64 | + android:layout_height="match_parent" |
65 | + android:orientation="horizontal" > |
66 | + |
67 | + <fragment |
68 | + android:id="@+id/settings_fragment" |
69 | + android:layout_width="match_parent" |
70 | + android:layout_height="match_parent" |
71 | + class="com.ubuntuone.android.music.ui.SettingsFragment" /> |
72 | + |
73 | +</LinearLayout> |
74 | \ No newline at end of file |
75 | |
76 | === added file 'main/res/layout/seek_bar_preference.xml' |
77 | --- main/res/layout/seek_bar_preference.xml 1970-01-01 00:00:00 +0000 |
78 | +++ main/res/layout/seek_bar_preference.xml 2013-01-25 16:20:29 +0000 |
79 | @@ -0,0 +1,54 @@ |
80 | +<?xml version="1.0" encoding="utf-8"?> |
81 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
82 | + android:id="@android:id/widget_frame" |
83 | + android:layout_width="fill_parent" |
84 | + android:layout_height="wrap_content" |
85 | + android:layout_marginBottom="6dp" |
86 | + android:layout_marginLeft="15dp" |
87 | + android:layout_marginRight="6dp" |
88 | + android:layout_marginTop="6dp" |
89 | + android:gravity="center_vertical" |
90 | + android:minHeight="?android:attr/listPreferredItemHeight" |
91 | + android:paddingLeft="8dp" |
92 | + android:paddingRight="?android:attr/scrollbarSize" > |
93 | + |
94 | + <TextView |
95 | + android:id="@android:id/title" |
96 | + android:layout_width="fill_parent" |
97 | + android:layout_height="wrap_content" |
98 | + android:layout_alignParentLeft="true" |
99 | + android:layout_alignParentTop="true" |
100 | + android:ellipsize="marquee" |
101 | + android:fadingEdge="horizontal" |
102 | + android:singleLine="true" |
103 | + android:textAppearance="?android:attr/textAppearanceMedium" /> |
104 | + |
105 | + <TextView |
106 | + android:id="@android:id/summary" |
107 | + android:layout_width="fill_parent" |
108 | + android:layout_height="wrap_content" |
109 | + android:layout_alignParentLeft="true" |
110 | + android:layout_below="@android:id/title" |
111 | + android:textAppearance="?android:attr/textAppearanceSmall" |
112 | + android:textColor="?android:attr/textColorSecondary" /> |
113 | + |
114 | + <TextView |
115 | + android:id="@+id/seekBarPrefValue" |
116 | + android:layout_width="wrap_content" |
117 | + android:layout_height="wrap_content" |
118 | + android:layout_below="@android:id/title" |
119 | + android:layout_alignParentRight="true" |
120 | + android:gravity="right" |
121 | + android:textAppearance="?android:attr/textAppearanceSmall" |
122 | + android:textColor="?android:attr/textColorSecondary" /> |
123 | + |
124 | + <LinearLayout |
125 | + android:id="@+id/seekBarPrefBarContainer" |
126 | + android:layout_width="fill_parent" |
127 | + android:layout_height="wrap_content" |
128 | + android:layout_alignParentBottom="true" |
129 | + android:layout_alignParentLeft="true" |
130 | + android:layout_below="@android:id/summary" > |
131 | + </LinearLayout> |
132 | + |
133 | +</RelativeLayout> |
134 | \ No newline at end of file |
135 | |
136 | === added directory 'main/res/xml' |
137 | === added file 'main/res/xml/preference_headers.xml' |
138 | --- main/res/xml/preference_headers.xml 1970-01-01 00:00:00 +0000 |
139 | +++ main/res/xml/preference_headers.xml 2013-01-25 16:20:29 +0000 |
140 | @@ -0,0 +1,31 @@ |
141 | +<?xml version="1.0" encoding="utf-8"?> |
142 | +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > |
143 | + |
144 | + <header |
145 | + android:fragment="com.ubuntuone.android.music.ui.PreferencesActivity$PreferencesFragment" |
146 | + android:icon="@android:drawable/ic_menu_help" |
147 | + android:summary="(placeholder item)" |
148 | + android:title="Account" > |
149 | + <extra |
150 | + android:name="preferenceResource" |
151 | + android:value="preferences_account" /> |
152 | + </header> |
153 | + <header |
154 | + android:fragment="com.ubuntuone.android.music.ui.PreferencesActivity$PreferencesFragment" |
155 | + android:icon="@android:drawable/ic_menu_manage" |
156 | + android:summary="Manage used space" |
157 | + android:title="Cache" > |
158 | + <extra |
159 | + android:name="preferenceResource" |
160 | + android:value="preferences_cache" /> |
161 | + </header> |
162 | + <header |
163 | + android:icon="@android:drawable/ic_menu_agenda" |
164 | + android:summary="(placeholder item)" |
165 | + android:title="Ubuntu One Blog" > |
166 | + <intent |
167 | + android:action="android.intent.action.VIEW" |
168 | + android:data="https://one.ubuntu.com/blog" /> |
169 | + </header> |
170 | + |
171 | +</preference-headers> |
172 | \ No newline at end of file |
173 | |
174 | === added file 'main/res/xml/preferences_account.xml' |
175 | --- main/res/xml/preferences_account.xml 1970-01-01 00:00:00 +0000 |
176 | +++ main/res/xml/preferences_account.xml 2013-01-25 16:20:29 +0000 |
177 | @@ -0,0 +1,6 @@ |
178 | +<?xml version="1.0" encoding="utf-8"?> |
179 | +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" |
180 | + xmlns:ubuntuone="http://schemas.one.ubuntu.com/apk/res/android" |
181 | + android:key="preference_screen" > |
182 | + |
183 | +</PreferenceScreen> |
184 | \ No newline at end of file |
185 | |
186 | === added file 'main/res/xml/preferences_cache.xml' |
187 | --- main/res/xml/preferences_cache.xml 1970-01-01 00:00:00 +0000 |
188 | +++ main/res/xml/preferences_cache.xml 2013-01-25 16:20:29 +0000 |
189 | @@ -0,0 +1,26 @@ |
190 | +<?xml version="1.0" encoding="utf-8"?> |
191 | +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" |
192 | + xmlns:ubuntuone="http://schemas.one.ubuntu.com/apk/res/android" |
193 | + android:key="preference_screen" > |
194 | + |
195 | + <PreferenceCategory |
196 | + android:key="preferenceCategory" |
197 | + android:title="Cache" > |
198 | + <ListPreference |
199 | + android:key="cacheLocation" |
200 | + android:persistent="true" |
201 | + android:title="Storage" /> |
202 | + |
203 | + <com.ubuntuone.android.music.util.SeekBarPreference |
204 | + android:key="cacheSize" |
205 | + android:persistent="true" |
206 | + android:summary="Adjust allowed space" |
207 | + android:title="Size" /> |
208 | + |
209 | + <Preference |
210 | + android:key="clearCache" |
211 | + android:summary="Deletes non-favorite songs" |
212 | + android:title="Clear non-favorites" /> |
213 | + </PreferenceCategory> |
214 | + |
215 | +</PreferenceScreen> |
216 | \ No newline at end of file |
217 | |
218 | === added file 'main/src/com/ubuntuone/android/music/Constants.java' |
219 | --- main/src/com/ubuntuone/android/music/Constants.java 1970-01-01 00:00:00 +0000 |
220 | +++ main/src/com/ubuntuone/android/music/Constants.java 2013-01-25 16:20:29 +0000 |
221 | @@ -0,0 +1,10 @@ |
222 | +package com.ubuntuone.android.music; |
223 | + |
224 | +public class Constants { |
225 | + public static interface Preference { |
226 | + String CACHE_LOCATION_KEY = "cacheLocation"; |
227 | + String CACHE_DIR_KEY = "cacheDir"; |
228 | + String CACHE_SIZE_KEY = "cacheSize"; |
229 | + String CLEAR_CACHE_KEY = "clearCache"; |
230 | + } |
231 | +} |
232 | |
233 | === modified file 'main/src/com/ubuntuone/android/music/UbuntuOneMusic.java' |
234 | --- main/src/com/ubuntuone/android/music/UbuntuOneMusic.java 2013-01-23 02:36:20 +0000 |
235 | +++ main/src/com/ubuntuone/android/music/UbuntuOneMusic.java 2013-01-25 16:20:29 +0000 |
236 | @@ -37,6 +37,7 @@ |
237 | import com.ubuntuone.android.music.util.HttpManager; |
238 | import com.ubuntuone.android.music.util.Log; |
239 | import com.ubuntuone.android.music.util.NotAuthenticatedException; |
240 | +import com.ubuntuone.android.music.util.StorageUtils; |
241 | import com.ubuntuone.api.music.U1MusicAPI; |
242 | import com.ubuntuone.api.sso.authorizer.OAuthAuthorizer; |
243 | |
244 | @@ -67,6 +68,8 @@ |
245 | super.onCreate(); |
246 | instance = this; |
247 | setupLogging(); |
248 | + |
249 | + StorageUtils.setupMusicDirectory(this); |
250 | } |
251 | |
252 | private void setupLogging() { |
253 | |
254 | === modified file 'main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java' |
255 | --- main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2012-12-14 04:29:46 +0000 |
256 | +++ main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2013-01-25 16:20:29 +0000 |
257 | @@ -206,7 +206,25 @@ |
258 | String selection = Songs.SONG_PATH + " IS NOT NULL"; |
259 | return getSongCount(context, selection); |
260 | } |
261 | - |
262 | + |
263 | + public static long getCachedSongSize(Context context) { |
264 | + ContentResolver resolver = context.getContentResolver(); |
265 | + String[] projection = new String[] { |
266 | + "SUM(" + Songs.SONG_SIZE + ") as cache_size_used" |
267 | + }; |
268 | + Cursor cursor = null; |
269 | + long size = 0; |
270 | + try { |
271 | + cursor = resolver.query(Songs.CACHED_SONGS_URI, projection, null, null, null); |
272 | + if (cursor != null && cursor.moveToFirst()) { |
273 | + size = cursor.getLong(cursor.getColumnIndex("cache_size_used")); |
274 | + } |
275 | + } finally { |
276 | + if (cursor != null) cursor.close(); |
277 | + } |
278 | + return size; |
279 | + } |
280 | + |
281 | public static int getPlaylistSongCount(Context context, Uri playlistUri) { |
282 | ContentResolver resolver = context.getContentResolver(); |
283 | String playlistId = Playlists.getPlaylistId(playlistUri); |
284 | |
285 | === modified file 'main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java' |
286 | --- main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java 2012-12-14 04:29:46 +0000 |
287 | +++ main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java 2013-01-25 16:20:29 +0000 |
288 | @@ -56,6 +56,7 @@ |
289 | private MusicService mMusicService; |
290 | private BroadcastReceiver mHeadsetBroadcastReceiver; |
291 | private PhoneStateListener mPhoneStateListener; |
292 | + private boolean mWasOnCreateCalled = false; |
293 | |
294 | public MusicServiceSupport(MusicService service) { |
295 | this.mMusicService = service; |
296 | @@ -64,6 +65,7 @@ |
297 | } |
298 | |
299 | public void onCreate() { |
300 | + mWasOnCreateCalled = true; |
301 | registerHeadsetBroadcastReceiver(); |
302 | registerMediaButtonBroadcastReceiver(); |
303 | registerPhoneStateListener(); |
304 | @@ -79,6 +81,9 @@ |
305 | } |
306 | |
307 | public void onDestroy() { |
308 | + if (!mWasOnCreateCalled) { |
309 | + return; |
310 | + } |
311 | unregisterHeadsetBroadcastReceiver(); |
312 | unregisterMediaButtonBroadcastReceiver(); |
313 | unregisterPhoneStateListener(); |
314 | |
315 | === modified file 'main/src/com/ubuntuone/android/music/ui/HomeActivity.java' |
316 | --- main/src/com/ubuntuone/android/music/ui/HomeActivity.java 2013-01-23 02:36:20 +0000 |
317 | +++ main/src/com/ubuntuone/android/music/ui/HomeActivity.java 2013-01-25 16:20:29 +0000 |
318 | @@ -419,7 +419,7 @@ |
319 | } |
320 | |
321 | private boolean onSettingsOptionItemSelected() { |
322 | - Intent settingsIntent = new Intent(this, SettingsActivity.class); |
323 | + Intent settingsIntent = new Intent(this, PreferencesActivity.class); |
324 | startActivity(settingsIntent); |
325 | return true; |
326 | } |
327 | |
328 | === added file 'main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java' |
329 | --- main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java 1970-01-01 00:00:00 +0000 |
330 | +++ main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java 2013-01-25 16:20:29 +0000 |
331 | @@ -0,0 +1,269 @@ |
332 | +/* |
333 | + * Ubuntu One Music - stream music from Ubuntu One cloud storage. |
334 | + * |
335 | + * Copyright 2012-2013 Canonical Ltd. |
336 | + * |
337 | + * This file is part of Ubuntu One Music app. |
338 | + * |
339 | + * This program is free software: you can redistribute it and/or modify |
340 | + * it under the terms of the GNU Affero General Public License as |
341 | + * published by the Free Software Foundation, either version 3 of the |
342 | + * License, or (at your option) any later version. |
343 | + * |
344 | + * This program is distributed in the hope that it will be useful, |
345 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
346 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
347 | + * GNU Affero General Public License for more details. |
348 | + * |
349 | + * You should have received a copy of the GNU Affero General Public License |
350 | + * along with this program. If not, see http://www.gnu.org/licenses |
351 | + */ |
352 | + |
353 | +package com.ubuntuone.android.music.ui; |
354 | + |
355 | +import static com.ubuntuone.android.music.util.LogUtils.makeLogTag; |
356 | +import static com.ubuntuone.android.music.util.StorageUtils.MiB; |
357 | + |
358 | +import java.io.File; |
359 | +import java.lang.reflect.InvocationTargetException; |
360 | +import java.lang.reflect.Method; |
361 | +import java.util.Arrays; |
362 | +import java.util.List; |
363 | + |
364 | +import android.annotation.TargetApi; |
365 | +import android.content.ContentResolver; |
366 | +import android.content.ContentValues; |
367 | +import android.content.Context; |
368 | +import android.database.Cursor; |
369 | +import android.net.Uri; |
370 | +import android.os.AsyncTask; |
371 | +import android.os.Build; |
372 | +import android.os.Bundle; |
373 | +import android.preference.ListPreference; |
374 | +import android.preference.Preference; |
375 | +import android.preference.Preference.OnPreferenceChangeListener; |
376 | +import android.preference.Preference.OnPreferenceClickListener; |
377 | +import android.preference.PreferenceActivity; |
378 | +import android.preference.PreferenceFragment; |
379 | +import android.preference.PreferenceManager; |
380 | +import android.util.Log; |
381 | +import android.widget.Toast; |
382 | + |
383 | +import com.ubuntuone.android.music.Constants; |
384 | +import com.ubuntuone.android.music.R; |
385 | +import com.ubuntuone.android.music.provider.MusicContract.Songs; |
386 | +import com.ubuntuone.android.music.provider.MusicProviderUtils; |
387 | +import com.ubuntuone.android.music.util.SeekBarPreference; |
388 | +import com.ubuntuone.android.music.util.StorageUtils; |
389 | +import com.ubuntuone.android.music.util.StorageUtils.InvalidStorage; |
390 | +import com.ubuntuone.android.music.util.UIUtils; |
391 | + |
392 | +public class PreferencesActivity extends PreferenceActivity implements OnPreferenceClickListener { |
393 | + private static final String TAG = makeLogTag(PreferencesActivity.class.getSimpleName()); |
394 | + |
395 | + protected Method mLoadHeaders = null; |
396 | + protected Method mHasHeaders = null; |
397 | + protected boolean mIsTablet; |
398 | + |
399 | + protected ClearCacheTask mClearCacheTask; |
400 | + |
401 | + public boolean hasHeadersCompat() { |
402 | + if (mHasHeaders != null && mLoadHeaders != null) { |
403 | + try { |
404 | + return (Boolean) mHasHeaders.invoke(this); |
405 | + } catch (IllegalArgumentException e) { |
406 | + } catch (IllegalAccessException e) { |
407 | + } catch (InvocationTargetException e) { |
408 | + } |
409 | + } |
410 | + return false; |
411 | + } |
412 | + |
413 | + @SuppressWarnings("deprecation") |
414 | + @Override |
415 | + public void onCreate(Bundle aSavedState) { |
416 | + // onBuildHeaders() will be called during super.onCreate() |
417 | + try { |
418 | + mLoadHeaders = getClass().getMethod("loadHeadersFromResource", int.class, List.class); |
419 | + mHasHeaders = getClass().getMethod("hasHeaders"); |
420 | + } catch (NoSuchMethodException e) { |
421 | + // We're handling an old API. |
422 | + } |
423 | + mIsTablet = UIUtils.isTablet(this); |
424 | + super.onCreate(aSavedState); |
425 | + if (!hasHeadersCompat() || !mIsTablet) { |
426 | + addPreferencesFromResource(R.xml.preferences_account); |
427 | + addPreferencesFromResource(R.xml.preferences_cache); |
428 | + } |
429 | + |
430 | + setupCacheLocationPreference(); |
431 | + setupCacheSizePreference(this); |
432 | + setupClearCachePreference(this); |
433 | + } |
434 | + |
435 | + @Override |
436 | + public void onBuildHeaders(List<Header> target) { |
437 | + try { |
438 | + if (mIsTablet) { |
439 | + mLoadHeaders.invoke(this, new Object[] { R.xml.preference_headers, target }); |
440 | + } |
441 | + } catch (IllegalArgumentException e) { |
442 | + } catch (IllegalAccessException e) { |
443 | + } catch (InvocationTargetException e) { |
444 | + } |
445 | + } |
446 | + |
447 | + @TargetApi(Build.VERSION_CODES.HONEYCOMB) |
448 | + static public class PreferencesFragment extends PreferenceFragment { |
449 | + @Override |
450 | + public void onCreate(Bundle savedInstanceState) { |
451 | + super.onCreate(savedInstanceState); |
452 | + Context context = getActivity().getApplicationContext(); |
453 | + int preferencesResource = context.getResources().getIdentifier( |
454 | + getArguments().getString("preferenceResource"), "xml", |
455 | + context.getPackageName()); |
456 | + PreferenceManager.setDefaultValues(context, preferencesResource, false); |
457 | + addPreferencesFromResource(preferencesResource); |
458 | + // Hack. On tablets, hide the category title duplicated in fragment title. |
459 | + findPreference("preferenceCategory").setTitle(""); |
460 | + } |
461 | + } |
462 | + |
463 | + public void setupCacheLocationPreference() { |
464 | + @SuppressWarnings("deprecation") |
465 | + final ListPreference cacheLocation = (ListPreference) findPreference( |
466 | + Constants.Preference.CACHE_LOCATION_KEY); |
467 | + final String[] storagePaths = StorageUtils.getAvailableStoragePaths(); |
468 | + Log.i(TAG, "Available storage paths: " + Arrays.toString(storagePaths)); |
469 | + if (storagePaths != null) { |
470 | + cacheLocation.setEntries(storagePaths); |
471 | + cacheLocation.setEntryValues(storagePaths); |
472 | + |
473 | + String storage = cacheLocation.getValue(); |
474 | + cacheLocation.setValue(storage); |
475 | + cacheLocation.setSummary(storage); |
476 | + try { |
477 | + int index = StorageUtils.getSelectedStorageIndex(storage, storagePaths); |
478 | + cacheLocation.setValueIndex(index); |
479 | + } catch (InvalidStorage e) { |
480 | + // Should not happen, as the value has been already set. |
481 | + } |
482 | + |
483 | + cacheLocation.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { |
484 | + @Override |
485 | + public boolean onPreferenceChange(Preference preference, Object newValue) { |
486 | + // TODO karni: Move cached music to new location or at least notify the user. |
487 | + String location = (String) newValue; |
488 | + cacheLocation.setSummary(location); |
489 | + return true; |
490 | + } |
491 | + }); |
492 | + } else { |
493 | + cacheLocation.setEnabled(false); |
494 | + } |
495 | + } |
496 | + |
497 | + public void setupCacheSizePreference(Context context) { |
498 | + @SuppressWarnings("deprecation") |
499 | + SeekBarPreference seekBarPreference = (SeekBarPreference) findPreference( |
500 | + Constants.Preference.CACHE_SIZE_KEY); |
501 | + String storage = StorageUtils.getMusicDirectory(context); |
502 | + |
503 | + long cacheSize = MusicProviderUtils.getCachedSongSize(context); |
504 | + long min = Math.max(StorageUtils.MIN_CACHE_SIZE, cacheSize); |
505 | + long max = StorageUtils.getAvailableStorage(context, storage) - cacheSize; |
506 | + |
507 | + seekBarPreference.setMinValue((int)(min / MiB)); |
508 | + seekBarPreference.setMaxValue((int)(max / MiB)); |
509 | + seekBarPreference.setDefaultValue((int)(max / MiB)); |
510 | + } |
511 | + |
512 | + public void setupClearCachePreference(Context context) { |
513 | + @SuppressWarnings("deprecation") |
514 | + Preference clearCachePreference = findPreference(Constants.Preference.CLEAR_CACHE_KEY); |
515 | + long cacheSizeUsed = MusicProviderUtils.getCachedSongSize(context); |
516 | + String fuzzySpaceUsed = StorageUtils.getFuzzySize(cacheSizeUsed); |
517 | + Log.i(TAG, String.format("Cache stats: songs %d used %s", |
518 | + MusicProviderUtils.getCachedSongCount(context), fuzzySpaceUsed)); |
519 | + clearCachePreference.setSummary(String.format("Space used: %s", fuzzySpaceUsed)); |
520 | + clearCachePreference.setOnPreferenceClickListener(this); |
521 | + } |
522 | + |
523 | + @Override |
524 | + public boolean onPreferenceClick(Preference preference) { |
525 | + String key = preference.getKey(); |
526 | + if (Constants.Preference.CLEAR_CACHE_KEY.equals(key)) { |
527 | + if (mClearCacheTask == null) { |
528 | + mClearCacheTask = new ClearCacheTask(); |
529 | + mClearCacheTask.execute(); |
530 | + } |
531 | + } |
532 | + return false; |
533 | + } |
534 | + |
535 | + public class ClearCacheTask extends AsyncTask<Integer, Void, Integer> { |
536 | + private Context mContext = getApplicationContext(); |
537 | + |
538 | + @Override |
539 | + protected void onPreExecute() { |
540 | + Toast.makeText(mContext, "Clearing cache...", Toast.LENGTH_SHORT).show(); |
541 | + } |
542 | + |
543 | + @Override |
544 | + protected Integer doInBackground(Integer... params) { |
545 | + ContentResolver resolver = mContext.getContentResolver(); |
546 | + String[] projection = new String[] { Songs.SONG_ID, Songs.SONG_PATH }; |
547 | + String selection = Songs.STARRED + "!=1"; |
548 | + |
549 | + Cursor cursor = null; |
550 | + Integer deletedSongs = 0; |
551 | + try { |
552 | + cursor = resolver.query(Songs.CACHED_SONGS_URI, projection, selection, null, null); |
553 | + if (cursor != null && cursor.moveToFirst()) { |
554 | + int pathColumnIndex = cursor.getColumnIndex(Songs.SONG_PATH); |
555 | + int songIdColumnIndex = cursor.getColumnIndex(Songs.SONG_ID); |
556 | + do { |
557 | + if (deleteSong(resolver, cursor, pathColumnIndex, songIdColumnIndex)) { |
558 | + deletedSongs++; |
559 | + } |
560 | + } while (cursor.moveToNext()); |
561 | + } |
562 | + } finally { |
563 | + if (cursor != null) cursor.close(); |
564 | + } |
565 | + return deletedSongs; |
566 | + } |
567 | + |
568 | + private boolean deleteSong(ContentResolver resolver, Cursor cursor, |
569 | + int pathColumnIndex, int songIdColumnIndex) { |
570 | + String path = cursor.getString(pathColumnIndex); |
571 | + String songId = cursor.getString(songIdColumnIndex); |
572 | + File songFile = new File(path); |
573 | + if (songFile.exists()) { |
574 | + Uri songUri = Songs.buildSongUri(songId); |
575 | + ContentValues values = new ContentValues(); |
576 | + values.putNull(Songs.SONG_PATH); |
577 | + boolean entryUpdated = resolver.update(songUri, values, null, null) == 1; |
578 | + return songFile.delete() && entryUpdated; |
579 | + } |
580 | + return false; |
581 | + } |
582 | + |
583 | + @Override |
584 | + protected void onPostExecute(Integer result) { |
585 | + Toast.makeText(mContext, String.format("Deleted %d songs.", result), |
586 | + Toast.LENGTH_SHORT).show(); |
587 | + mClearCacheTask = null; |
588 | + |
589 | + if (!isFinishing()) { |
590 | + setupClearCachePreference(PreferencesActivity.this); |
591 | + } |
592 | + } |
593 | + |
594 | + @Override |
595 | + protected void onCancelled() { |
596 | + Toast.makeText(mContext, "Canceled.", Toast.LENGTH_SHORT).show(); |
597 | + mClearCacheTask = null; |
598 | + } |
599 | + } |
600 | +} |
601 | |
602 | === removed file 'main/src/com/ubuntuone/android/music/ui/SettingsActivity.java' |
603 | --- main/src/com/ubuntuone/android/music/ui/SettingsActivity.java 2012-12-14 04:29:46 +0000 |
604 | +++ main/src/com/ubuntuone/android/music/ui/SettingsActivity.java 1970-01-01 00:00:00 +0000 |
605 | @@ -1,29 +0,0 @@ |
606 | -/* |
607 | - * Ubuntu One Music - stream music from Ubuntu One cloud storage. |
608 | - * |
609 | - * Copyright 2012 Canonical Ltd. |
610 | - * |
611 | - * This file is part of Ubuntu One Music app. |
612 | - * |
613 | - * This program is free software: you can redistribute it and/or modify |
614 | - * it under the terms of the GNU Affero General Public License as |
615 | - * published by the Free Software Foundation, either version 3 of the |
616 | - * License, or (at your option) any later version. |
617 | - * |
618 | - * This program is distributed in the hope that it will be useful, |
619 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
620 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
621 | - * GNU Affero General Public License for more details. |
622 | - * |
623 | - * You should have received a copy of the GNU Affero General Public License |
624 | - * along with this program. If not, see http://www.gnu.org/licenses |
625 | - */ |
626 | - |
627 | -package com.ubuntuone.android.music.ui; |
628 | - |
629 | -import com.actionbarsherlock.app.SherlockPreferenceActivity; |
630 | - |
631 | -public class SettingsActivity extends SherlockPreferenceActivity |
632 | -{ |
633 | - |
634 | -} |
635 | |
636 | === modified file 'main/src/com/ubuntuone/android/music/util/AccountUtils.java' |
637 | --- main/src/com/ubuntuone/android/music/util/AccountUtils.java 2013-01-23 02:36:20 +0000 |
638 | +++ main/src/com/ubuntuone/android/music/util/AccountUtils.java 2013-01-25 16:20:29 +0000 |
639 | @@ -92,11 +92,8 @@ |
640 | if (accounts.length > 0) { |
641 | return U1_ACCOUNT_TYPE; |
642 | } |
643 | - accounts = accountManager.getAccountsByType(OLD_U1_ACCOUNT_TYPE); |
644 | - if (accounts.length > 0) { |
645 | - return U1_ACCOUNT_TYPE; |
646 | - } |
647 | - return null; |
648 | + // Otherwise, always return the old account type, regardless if the account exists. |
649 | + return OLD_U1_ACCOUNT_TYPE; |
650 | } |
651 | |
652 | public static String getChosenAccountName(final Context context) { |
653 | |
654 | === added file 'main/src/com/ubuntuone/android/music/util/SeekBarPreference.java' |
655 | --- main/src/com/ubuntuone/android/music/util/SeekBarPreference.java 1970-01-01 00:00:00 +0000 |
656 | +++ main/src/com/ubuntuone/android/music/util/SeekBarPreference.java 2013-01-25 16:20:29 +0000 |
657 | @@ -0,0 +1,195 @@ |
658 | +package com.ubuntuone.android.music.util; |
659 | + |
660 | +import static com.ubuntuone.android.music.util.StorageUtils.MiB; |
661 | +import android.content.Context; |
662 | +import android.content.res.TypedArray; |
663 | +import android.preference.Preference; |
664 | +import android.util.AttributeSet; |
665 | +import android.util.Log; |
666 | +import android.view.LayoutInflater; |
667 | +import android.view.View; |
668 | +import android.view.ViewGroup; |
669 | +import android.view.ViewParent; |
670 | +import android.widget.RelativeLayout; |
671 | +import android.widget.SeekBar; |
672 | +import android.widget.SeekBar.OnSeekBarChangeListener; |
673 | +import android.widget.TextView; |
674 | + |
675 | +import com.ubuntuone.android.music.R; |
676 | + |
677 | +public class SeekBarPreference extends Preference implements |
678 | + OnSeekBarChangeListener { |
679 | + private final String TAG = getClass().getName(); |
680 | + |
681 | + private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android"; |
682 | + private static final String UBUNTUONE_NS = "http://schemas.one.ubuntu.com/apk/res/android"; |
683 | + private static final int DEFAULT_VALUE = 50; |
684 | + |
685 | + private int mMaxValue = 100; |
686 | + private int mMinValue = 0; |
687 | + private int mInterval = 1; |
688 | + private int mCurrentValue; |
689 | + private SeekBar mSeekBar; |
690 | + |
691 | + private TextView mStatusText; |
692 | + |
693 | + public void setMinValue(int min) { |
694 | + mMinValue = min; |
695 | + mSeekBar.setMax(mMaxValue - mMinValue); |
696 | + } |
697 | + |
698 | + public void setMaxValue(int max) { |
699 | + mMaxValue = max; |
700 | + mSeekBar.setMax(mMaxValue - mMinValue); |
701 | + } |
702 | + |
703 | + public SeekBarPreference(Context context, AttributeSet attrs) { |
704 | + super(context, attrs); |
705 | + initPreference(context, attrs); |
706 | + } |
707 | + |
708 | + public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) { |
709 | + super(context, attrs, defStyle); |
710 | + initPreference(context, attrs); |
711 | + } |
712 | + |
713 | + private void initPreference(Context context, AttributeSet attrs) { |
714 | + setValuesFromXml(attrs); |
715 | + mSeekBar = new SeekBar(context, attrs); |
716 | + mSeekBar.setMax(mMaxValue - mMinValue); |
717 | + mSeekBar.setOnSeekBarChangeListener(this); |
718 | + } |
719 | + |
720 | + private void setValuesFromXml(AttributeSet attrs) { |
721 | + mMaxValue = attrs.getAttributeIntValue(ANDROID_NS, "max", 100); |
722 | + mMinValue = attrs.getAttributeIntValue(UBUNTUONE_NS, "min", 0); |
723 | + |
724 | + try { |
725 | + String newInterval = attrs.getAttributeValue(UBUNTUONE_NS, "interval"); |
726 | + if (newInterval != null) { |
727 | + mInterval = Integer.parseInt(newInterval); |
728 | + } |
729 | + } catch (Exception e) { |
730 | + Log.e(TAG, "Invalid interval value", e); |
731 | + } |
732 | + |
733 | + } |
734 | + |
735 | + @SuppressWarnings("unused") |
736 | + private String getAttributeStringValue(AttributeSet attrs, |
737 | + String namespace, String name, String defaultValue) { |
738 | + String value = attrs.getAttributeValue(namespace, name); |
739 | + if (value == null) { |
740 | + value = defaultValue; |
741 | + } |
742 | + return value; |
743 | + } |
744 | + |
745 | + @Override |
746 | + protected View onCreateView(ViewGroup parent) { |
747 | + RelativeLayout layout = null; |
748 | + try { |
749 | + LayoutInflater mInflater = (LayoutInflater) getContext() |
750 | + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
751 | + |
752 | + layout = (RelativeLayout) mInflater.inflate( |
753 | + R.layout.seek_bar_preference, parent, false); |
754 | + } catch (Exception e) { |
755 | + Log.e(TAG, "Error creating seek bar preference", e); |
756 | + } |
757 | + return layout; |
758 | + } |
759 | + |
760 | + @Override |
761 | + public void onBindView(View view) { |
762 | + super.onBindView(view); |
763 | + try { |
764 | + ViewParent oldContainer = mSeekBar.getParent(); |
765 | + ViewGroup newContainer = (ViewGroup) view |
766 | + .findViewById(R.id.seekBarPrefBarContainer); |
767 | + |
768 | + if (oldContainer != newContainer) { |
769 | + if (oldContainer != null) { |
770 | + ((ViewGroup) oldContainer).removeView(mSeekBar); |
771 | + } |
772 | + newContainer.removeAllViews(); |
773 | + newContainer.addView(mSeekBar, |
774 | + ViewGroup.LayoutParams.MATCH_PARENT, |
775 | + ViewGroup.LayoutParams.WRAP_CONTENT); |
776 | + } |
777 | + } catch (Exception ex) { |
778 | + Log.e(TAG, "Error binding view: " + ex.toString()); |
779 | + } |
780 | + updateView(view); |
781 | + } |
782 | + |
783 | + /** |
784 | + * Update a SeekBarPreference view with our current state |
785 | + * |
786 | + * @param view |
787 | + */ |
788 | + protected void updateView(View view) { |
789 | + |
790 | + try { |
791 | + RelativeLayout layout = (RelativeLayout) view; |
792 | + mStatusText = (TextView) layout.findViewById(R.id.seekBarPrefValue); |
793 | + mStatusText.setMinimumWidth(30); |
794 | + mSeekBar.setProgress(mCurrentValue - mMinValue); |
795 | + |
796 | + mStatusText.setText(StorageUtils.getFuzzySize(mCurrentValue * MiB)); |
797 | + } catch (Exception e) { |
798 | + Log.e(TAG, "Error updating seek bar preference", e); |
799 | + } |
800 | + |
801 | + } |
802 | + |
803 | + @Override |
804 | + public void onProgressChanged(SeekBar seekBar, int progress, |
805 | + boolean fromUser) { |
806 | + int newValue = progress + mMinValue; |
807 | + |
808 | + if (newValue > mMaxValue) |
809 | + newValue = mMaxValue; |
810 | + else if (newValue < mMinValue) |
811 | + newValue = mMinValue; |
812 | + else if (mInterval != 1 && newValue % mInterval != 0) |
813 | + newValue = Math.round(((float) newValue) / mInterval) * mInterval; |
814 | + |
815 | + mCurrentValue = newValue; |
816 | + if (mStatusText != null) { |
817 | + mStatusText.setText(StorageUtils.getFuzzySize(newValue * MiB)); |
818 | + } |
819 | + persistInt(newValue); |
820 | + } |
821 | + |
822 | + @Override |
823 | + public void onStartTrackingTouch(SeekBar seekBar) { |
824 | + } |
825 | + |
826 | + @Override |
827 | + public void onStopTrackingTouch(SeekBar seekBar) { |
828 | + notifyChanged(); |
829 | + } |
830 | + |
831 | + @Override |
832 | + protected Object onGetDefaultValue(TypedArray typedArray, int index) { |
833 | + int defaultValue = typedArray.getInt(index, DEFAULT_VALUE); |
834 | + return defaultValue; |
835 | + } |
836 | + |
837 | + @Override |
838 | + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { |
839 | + if (restoreValue) { |
840 | + mCurrentValue = getPersistedInt(mCurrentValue); |
841 | + } else { |
842 | + int value = 0; |
843 | + try { |
844 | + value = (Integer) defaultValue; |
845 | + } catch (Exception ex) { |
846 | + Log.e(TAG, "Invalid default value: " + defaultValue.toString()); |
847 | + } |
848 | + persistInt(value); |
849 | + mCurrentValue = value; |
850 | + } |
851 | + } |
852 | +} |
853 | |
854 | === added file 'main/src/com/ubuntuone/android/music/util/StorageUtils.java' |
855 | --- main/src/com/ubuntuone/android/music/util/StorageUtils.java 1970-01-01 00:00:00 +0000 |
856 | +++ main/src/com/ubuntuone/android/music/util/StorageUtils.java 2013-01-25 16:20:29 +0000 |
857 | @@ -0,0 +1,216 @@ |
858 | +/* |
859 | + * Ubuntu One Music - stream music from Ubuntu One cloud storage. |
860 | + * |
861 | + * Copyright 2013 Canonical Ltd. |
862 | + * |
863 | + * This file is part of Ubuntu One Music app. |
864 | + * |
865 | + * This program is free software: you can redistribute it and/or modify |
866 | + * it under the terms of the GNU Affero General Public License as |
867 | + * published by the Free Software Foundation, either version 3 of the |
868 | + * License, or (at your option) any later version. |
869 | + * |
870 | + * This program is distributed in the hope that it will be useful, |
871 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
872 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
873 | + * GNU Affero General Public License for more details. |
874 | + * |
875 | + * You should have received a copy of the GNU Affero General Public License |
876 | + * along with this program. If not, see http://www.gnu.org/licenses |
877 | + */ |
878 | + |
879 | +package com.ubuntuone.android.music.util; |
880 | + |
881 | +import static com.ubuntuone.android.music.util.LogUtils.makeLogTag; |
882 | + |
883 | +import java.io.File; |
884 | +import java.util.ArrayList; |
885 | +import java.util.Arrays; |
886 | +import java.util.Locale; |
887 | + |
888 | +import android.annotation.SuppressLint; |
889 | +import android.content.Context; |
890 | +import android.content.SharedPreferences; |
891 | +import android.os.Build; |
892 | +import android.os.Environment; |
893 | +import android.os.StatFs; |
894 | +import android.preference.PreferenceManager; |
895 | +import android.util.Log; |
896 | + |
897 | +import com.ubuntuone.android.music.Constants; |
898 | +import com.ubuntuone.android.music.Constants.Preference; |
899 | + |
900 | +/** Utility functions to query and store information about the SDCard "external storage". */ |
901 | +public final class StorageUtils { |
902 | + private static final String TAG = makeLogTag(StorageUtils.class.getSimpleName()); |
903 | + |
904 | + public static final String MUSIC_DIR = "u1music"; |
905 | + |
906 | + public static final long MiB = 1024*1024; |
907 | + public static final long MIN_CACHE_SIZE = 100 * MiB; |
908 | + |
909 | + private StorageUtils() {} |
910 | + |
911 | + public static void setupMusicDirectory(Context context) { |
912 | + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); |
913 | + String storage = preferences.getString(Preference.CACHE_LOCATION_KEY, null); |
914 | + if (storage == null) { |
915 | + final String[] storagePaths = StorageUtils.getAvailableStoragePaths(); |
916 | + Log.i(TAG, "Available storage paths: " + Arrays.toString(storagePaths)); |
917 | + if (storagePaths != null) { |
918 | + storage = storagePaths[0]; |
919 | + } else { |
920 | + storage = getGenericCacheDirectory(); |
921 | + } |
922 | + PreferenceManager.getDefaultSharedPreferences(context).edit() |
923 | + .putString(Constants.Preference.CACHE_LOCATION_KEY, storage).commit(); |
924 | + |
925 | + } else { |
926 | + Log.w(TAG, "No storage paths matched on " + Build.MODEL); |
927 | + } |
928 | + |
929 | + long avail = getAvailableStorage(context, storage); |
930 | + long total = getTotalStorage(context, storage); |
931 | + Log.i(TAG, String.format("Storage stats: avail %s total %s", |
932 | + getFuzzySize(avail), getFuzzySize(total))); |
933 | + } |
934 | + |
935 | + /** |
936 | + * Returns the directory used to store cached music collection located on the selected storage. |
937 | + * <br /> |
938 | + * Prerequisite: <b>setupMusicDirectory</b> must be called before calling this method, |
939 | + * to initialize the default value of cache location preference. |
940 | + * |
941 | + * @param context |
942 | + * |
943 | + * @return Absolute path of the music storage directory. |
944 | + */ |
945 | + public static String getMusicDirectory(Context context) { |
946 | + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); |
947 | + String cacheStorage = preferences.getString(Preference.CACHE_LOCATION_KEY, null); |
948 | + String cacheDirectory = preferences.getString(Preference.CACHE_DIR_KEY, MUSIC_DIR); |
949 | + if (cacheStorage == null) { |
950 | + throw new IllegalStateException("storage is null, have you called setupMusicDirectory?"); |
951 | + } |
952 | + File storageDirectory = new File(cacheStorage); |
953 | + File musicDirectory = new File(storageDirectory, cacheDirectory); |
954 | + if (!musicDirectory.exists()) { |
955 | + musicDirectory.mkdirs(); |
956 | + } |
957 | + return musicDirectory.getAbsolutePath(); |
958 | + } |
959 | + |
960 | + /** |
961 | + * @return Absolute path of the default, generic music storage directory. |
962 | + */ |
963 | + public static String getGenericCacheDirectory() { |
964 | + final File storageDirectory = Environment.getExternalStorageDirectory(); |
965 | + final File genericCache = new File(storageDirectory, |
966 | + "Android/data/com.ubuntuone.android.music/files/cache"); |
967 | + if (!genericCache.isDirectory()) { |
968 | + Log.i(TAG, "Creating cache directory."); |
969 | + genericCache.delete(); // Doesn't throw IOException |
970 | + genericCache.mkdirs(); |
971 | + } |
972 | + return genericCache.getAbsolutePath(); |
973 | + } |
974 | + |
975 | + public static String getExternalStoragePath() { |
976 | + return Environment.getExternalStorageDirectory().getAbsolutePath(); |
977 | + } |
978 | + |
979 | + @SuppressLint("SdCardPath") // Yada yada lint. getExternalStorageDirectory() is NOT enough. |
980 | + private static final String[] knownStoragePaths = new String[] { |
981 | + "/storage/sdcard0", |
982 | + "/mnt/sdcard", |
983 | + "/mnt/sdcard2", |
984 | + "/mnt/sdcard-ext", // Motorola |
985 | + "/mnt/sdcard/external_sd", // Samsung |
986 | + "/mnt/ext_card", // Samsung |
987 | + "/sdcard/_ExternalSD" |
988 | + }; |
989 | + |
990 | + public static String[] getAvailableStoragePaths() { |
991 | + ArrayList<String> pathList = new ArrayList<String>(); |
992 | + for (String path : knownStoragePaths) { |
993 | + File storage = new File(path); |
994 | + if (storage.exists() && storage.isDirectory()) { |
995 | + pathList.add(storage.getAbsolutePath()); |
996 | + } |
997 | + } |
998 | + int size = pathList.size(); |
999 | + return pathList.toArray(new String[size]); |
1000 | + } |
1001 | + |
1002 | + public static int getSelectedStorageIndex(String storage, |
1003 | + String[] storagePaths) throws InvalidStorage { |
1004 | + for (int i = 0; i < storagePaths.length; i++) { |
1005 | + if (storage.equals(storagePaths[i])) |
1006 | + return i; |
1007 | + } |
1008 | + throw new InvalidStorage(); |
1009 | + } |
1010 | + |
1011 | + public static long getAvailableStorage(Context context, String storage) { |
1012 | + StatFs stat = new StatFs(getMusicDirectory(context)); |
1013 | + long blocksAvailable = stat.getAvailableBlocks(); |
1014 | + long blockSizeBytes = stat.getBlockSize(); |
1015 | + return blocksAvailable * blockSizeBytes; |
1016 | + } |
1017 | + |
1018 | + /** |
1019 | + * total space - free space - used cache size |
1020 | + * |
1021 | + * @param context |
1022 | + * @param storage |
1023 | + * @return |
1024 | + */ |
1025 | + public static long getAvailableCacheStorage(Context context, String storage, long cacheSize) { |
1026 | + StatFs stat = new StatFs(getMusicDirectory(context)); |
1027 | + long blocksAvailable = stat.getAvailableBlocks(); |
1028 | + long blockSizeBytes = stat.getBlockSize(); |
1029 | + long total = getTotalStorage(context, storage); |
1030 | + |
1031 | + long avail = blocksAvailable * blockSizeBytes; |
1032 | + return total - avail - cacheSize; |
1033 | + } |
1034 | + |
1035 | + public static long getTotalStorage(Context context, String storage) { |
1036 | + StatFs stat = new StatFs(getMusicDirectory(context)); |
1037 | + long blocksCount = stat.getBlockCount(); |
1038 | + long blockSizeBytes = stat.getBlockSize(); |
1039 | + return blocksCount * blockSizeBytes; |
1040 | + } |
1041 | + |
1042 | + public static String getFuzzySize(long size) { |
1043 | + String units = "B"; |
1044 | + String formatString = "%.0f %s"; |
1045 | + double s = size; |
1046 | + if (s > 1024.0) { |
1047 | + s /= 1024.0; |
1048 | + units = "KiB"; |
1049 | + } |
1050 | + if (s > 1024.0) { |
1051 | + s /= 1024.0; |
1052 | + units = "MiB"; |
1053 | + formatString = "%.02f %s"; |
1054 | + } |
1055 | + if (s > 1024.0) { |
1056 | + s /= 1024.0; |
1057 | + units = "GiB"; |
1058 | + } |
1059 | + return String.format(Locale.US, formatString, s, units); |
1060 | + } |
1061 | + |
1062 | + public static class InvalidStorage extends Exception { |
1063 | + private static final long serialVersionUID = -3042684172473773638L; |
1064 | + public InvalidStorage() { super(); } |
1065 | + public InvalidStorage(String msg) { super(msg); } |
1066 | + } |
1067 | + |
1068 | + public static class NoTertiaryStorage extends Exception { |
1069 | + private static final long serialVersionUID = -5605241960717201218L; |
1070 | + public NoTertiaryStorage() { super(); } |
1071 | + public NoTertiaryStorage(String msg) { super(msg); } |
1072 | + } |
1073 | +} |
+1