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