Merge lp:~karni/ubuntuone-android-music/v2-cache-settings into lp:ubuntuone-android-music/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
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.
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'main/AndroidManifest.xml'
--- main/AndroidManifest.xml 2013-01-23 02:37:15 +0000
+++ main/AndroidManifest.xml 2013-01-25 16:20:29 +0000
@@ -1,19 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>1<?xml version="1.0" encoding="utf-8"?>
2<manifest xmlns:android="http://schemas.android.com/apk/res/android"2<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.ubuntuone.android.music"3 package="com.ubuntuone.android.music"
4 android:sharedUserId="com.ubuntu.one"
5 android:sharedUserLabel="@string/shared_user"
4 android:versionCode="1"6 android:versionCode="1"
5 android:versionName="0.1.0" >7 android:versionName="0.1.0" >
68
7 <!--
8 FIXME Set shared user id:
9 android:sharedUserId="com.ubuntuone.android"
10 android:sharedUserLabel="@string/shared_user"
11 -->
12 <uses-sdk9 <uses-sdk
13 android:minSdkVersion="10"10 android:minSdkVersion="10"
14 android:targetSdkVersion="14" />11 android:targetSdkVersion="14" />
1512
16 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />13 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
14 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
17 <uses-permission android:name="android.permission.GET_ACCOUNTS" />15 <uses-permission android:name="android.permission.GET_ACCOUNTS" />
18 <uses-permission android:name="android.permission.INTERNET" />16 <uses-permission android:name="android.permission.INTERNET" />
19 <uses-permission android:name="android.permission.READ_PHONE_STATE" />17 <uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -23,6 +21,7 @@
2321
24 <application22 <application
25 android:name=".UbuntuOneMusic"23 android:name=".UbuntuOneMusic"
24 android:allowBackup="false"
26 android:label="@string/app_name"25 android:label="@string/app_name"
27 android:theme="@style/Theme.Sherlock.Light"26 android:theme="@style/Theme.Sherlock.Light"
28 android:uiOptions="splitActionBarWhenNarrow" >27 android:uiOptions="splitActionBarWhenNarrow" >
@@ -75,8 +74,8 @@
75 android:screenOrientation="portrait" >74 android:screenOrientation="portrait" >
76 </activity>75 </activity>
77 <activity76 <activity
78 android:name=".ui.SettingsActivity"77 android:name=".ui.PreferencesActivity"
79 android:label="U1 Music Settings"78 android:label="Ubuntu One Music Settings"
80 android:theme="@style/Theme.Sherlock.Light" >79 android:theme="@style/Theme.Sherlock.Light" >
81 </activity>80 </activity>
8281
@@ -95,8 +94,8 @@
9594
96 <provider95 <provider
97 android:name=".provider.MusicProvider"96 android:name=".provider.MusicProvider"
97 android:authorities="com.ubuntuone.android.music"
98 android:exported="false"98 android:exported="false"
99 android:authorities="com.ubuntuone.android.music"
100 android:label="@string/app_name"99 android:label="@string/app_name"
101 android:syncable="true"100 android:syncable="true"
102 android:writePermission="com.ubuntuone.android.music.permission.WRITE_MUSIC_DATA" />101 android:writePermission="com.ubuntuone.android.music.permission.WRITE_MUSIC_DATA" />
103102
=== added file 'main/res/layout/activity_preferences.xml'
--- main/res/layout/activity_preferences.xml 1970-01-01 00:00:00 +0000
+++ main/res/layout/activity_preferences.xml 2013-01-25 16:20:29 +0000
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="horizontal" >
6
7 <fragment
8 android:id="@+id/settings_fragment"
9 android:layout_width="match_parent"
10 android:layout_height="match_parent"
11 class="com.ubuntuone.android.music.ui.SettingsFragment" />
12
13</LinearLayout>
0\ No newline at end of file14\ No newline at end of file
115
=== added file 'main/res/layout/seek_bar_preference.xml'
--- main/res/layout/seek_bar_preference.xml 1970-01-01 00:00:00 +0000
+++ main/res/layout/seek_bar_preference.xml 2013-01-25 16:20:29 +0000
@@ -0,0 +1,54 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:id="@android:id/widget_frame"
4 android:layout_width="fill_parent"
5 android:layout_height="wrap_content"
6 android:layout_marginBottom="6dp"
7 android:layout_marginLeft="15dp"
8 android:layout_marginRight="6dp"
9 android:layout_marginTop="6dp"
10 android:gravity="center_vertical"
11 android:minHeight="?android:attr/listPreferredItemHeight"
12 android:paddingLeft="8dp"
13 android:paddingRight="?android:attr/scrollbarSize" >
14
15 <TextView
16 android:id="@android:id/title"
17 android:layout_width="fill_parent"
18 android:layout_height="wrap_content"
19 android:layout_alignParentLeft="true"
20 android:layout_alignParentTop="true"
21 android:ellipsize="marquee"
22 android:fadingEdge="horizontal"
23 android:singleLine="true"
24 android:textAppearance="?android:attr/textAppearanceMedium" />
25
26 <TextView
27 android:id="@android:id/summary"
28 android:layout_width="fill_parent"
29 android:layout_height="wrap_content"
30 android:layout_alignParentLeft="true"
31 android:layout_below="@android:id/title"
32 android:textAppearance="?android:attr/textAppearanceSmall"
33 android:textColor="?android:attr/textColorSecondary" />
34
35 <TextView
36 android:id="@+id/seekBarPrefValue"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:layout_below="@android:id/title"
40 android:layout_alignParentRight="true"
41 android:gravity="right"
42 android:textAppearance="?android:attr/textAppearanceSmall"
43 android:textColor="?android:attr/textColorSecondary" />
44
45 <LinearLayout
46 android:id="@+id/seekBarPrefBarContainer"
47 android:layout_width="fill_parent"
48 android:layout_height="wrap_content"
49 android:layout_alignParentBottom="true"
50 android:layout_alignParentLeft="true"
51 android:layout_below="@android:id/summary" >
52 </LinearLayout>
53
54</RelativeLayout>
0\ No newline at end of file55\ No newline at end of file
156
=== added directory 'main/res/xml'
=== added file 'main/res/xml/preference_headers.xml'
--- main/res/xml/preference_headers.xml 1970-01-01 00:00:00 +0000
+++ main/res/xml/preference_headers.xml 2013-01-25 16:20:29 +0000
@@ -0,0 +1,31 @@
1<?xml version="1.0" encoding="utf-8"?>
2<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
3
4 <header
5 android:fragment="com.ubuntuone.android.music.ui.PreferencesActivity$PreferencesFragment"
6 android:icon="@android:drawable/ic_menu_help"
7 android:summary="(placeholder item)"
8 android:title="Account" >
9 <extra
10 android:name="preferenceResource"
11 android:value="preferences_account" />
12 </header>
13 <header
14 android:fragment="com.ubuntuone.android.music.ui.PreferencesActivity$PreferencesFragment"
15 android:icon="@android:drawable/ic_menu_manage"
16 android:summary="Manage used space"
17 android:title="Cache" >
18 <extra
19 android:name="preferenceResource"
20 android:value="preferences_cache" />
21 </header>
22 <header
23 android:icon="@android:drawable/ic_menu_agenda"
24 android:summary="(placeholder item)"
25 android:title="Ubuntu One Blog" >
26 <intent
27 android:action="android.intent.action.VIEW"
28 android:data="https://one.ubuntu.com/blog" />
29 </header>
30
31</preference-headers>
0\ No newline at end of file32\ No newline at end of file
133
=== added file 'main/res/xml/preferences_account.xml'
--- main/res/xml/preferences_account.xml 1970-01-01 00:00:00 +0000
+++ main/res/xml/preferences_account.xml 2013-01-25 16:20:29 +0000
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?>
2<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:ubuntuone="http://schemas.one.ubuntu.com/apk/res/android"
4 android:key="preference_screen" >
5
6</PreferenceScreen>
0\ No newline at end of file7\ No newline at end of file
18
=== added file 'main/res/xml/preferences_cache.xml'
--- main/res/xml/preferences_cache.xml 1970-01-01 00:00:00 +0000
+++ main/res/xml/preferences_cache.xml 2013-01-25 16:20:29 +0000
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:ubuntuone="http://schemas.one.ubuntu.com/apk/res/android"
4 android:key="preference_screen" >
5
6 <PreferenceCategory
7 android:key="preferenceCategory"
8 android:title="Cache" >
9 <ListPreference
10 android:key="cacheLocation"
11 android:persistent="true"
12 android:title="Storage" />
13
14 <com.ubuntuone.android.music.util.SeekBarPreference
15 android:key="cacheSize"
16 android:persistent="true"
17 android:summary="Adjust allowed space"
18 android:title="Size" />
19
20 <Preference
21 android:key="clearCache"
22 android:summary="Deletes non-favorite songs"
23 android:title="Clear non-favorites" />
24 </PreferenceCategory>
25
26</PreferenceScreen>
0\ No newline at end of file27\ No newline at end of file
128
=== added file 'main/src/com/ubuntuone/android/music/Constants.java'
--- main/src/com/ubuntuone/android/music/Constants.java 1970-01-01 00:00:00 +0000
+++ main/src/com/ubuntuone/android/music/Constants.java 2013-01-25 16:20:29 +0000
@@ -0,0 +1,10 @@
1package com.ubuntuone.android.music;
2
3public class Constants {
4 public static interface Preference {
5 String CACHE_LOCATION_KEY = "cacheLocation";
6 String CACHE_DIR_KEY = "cacheDir";
7 String CACHE_SIZE_KEY = "cacheSize";
8 String CLEAR_CACHE_KEY = "clearCache";
9 }
10}
011
=== modified file 'main/src/com/ubuntuone/android/music/UbuntuOneMusic.java'
--- main/src/com/ubuntuone/android/music/UbuntuOneMusic.java 2013-01-23 02:36:20 +0000
+++ main/src/com/ubuntuone/android/music/UbuntuOneMusic.java 2013-01-25 16:20:29 +0000
@@ -37,6 +37,7 @@
37import com.ubuntuone.android.music.util.HttpManager;37import com.ubuntuone.android.music.util.HttpManager;
38import com.ubuntuone.android.music.util.Log;38import com.ubuntuone.android.music.util.Log;
39import com.ubuntuone.android.music.util.NotAuthenticatedException;39import com.ubuntuone.android.music.util.NotAuthenticatedException;
40import com.ubuntuone.android.music.util.StorageUtils;
40import com.ubuntuone.api.music.U1MusicAPI;41import com.ubuntuone.api.music.U1MusicAPI;
41import com.ubuntuone.api.sso.authorizer.OAuthAuthorizer;42import com.ubuntuone.api.sso.authorizer.OAuthAuthorizer;
4243
@@ -67,6 +68,8 @@
67 super.onCreate();68 super.onCreate();
68 instance = this;69 instance = this;
69 setupLogging();70 setupLogging();
71
72 StorageUtils.setupMusicDirectory(this);
70 }73 }
7174
72 private void setupLogging() {75 private void setupLogging() {
7376
=== modified file 'main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java'
--- main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2012-12-14 04:29:46 +0000
+++ main/src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2013-01-25 16:20:29 +0000
@@ -206,7 +206,25 @@
206 String selection = Songs.SONG_PATH + " IS NOT NULL";206 String selection = Songs.SONG_PATH + " IS NOT NULL";
207 return getSongCount(context, selection);207 return getSongCount(context, selection);
208 }208 }
209 209
210 public static long getCachedSongSize(Context context) {
211 ContentResolver resolver = context.getContentResolver();
212 String[] projection = new String[] {
213 "SUM(" + Songs.SONG_SIZE + ") as cache_size_used"
214 };
215 Cursor cursor = null;
216 long size = 0;
217 try {
218 cursor = resolver.query(Songs.CACHED_SONGS_URI, projection, null, null, null);
219 if (cursor != null && cursor.moveToFirst()) {
220 size = cursor.getLong(cursor.getColumnIndex("cache_size_used"));
221 }
222 } finally {
223 if (cursor != null) cursor.close();
224 }
225 return size;
226 }
227
210 public static int getPlaylistSongCount(Context context, Uri playlistUri) {228 public static int getPlaylistSongCount(Context context, Uri playlistUri) {
211 ContentResolver resolver = context.getContentResolver();229 ContentResolver resolver = context.getContentResolver();
212 String playlistId = Playlists.getPlaylistId(playlistUri);230 String playlistId = Playlists.getPlaylistId(playlistUri);
213231
=== modified file 'main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java'
--- main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java 2012-12-14 04:29:46 +0000
+++ main/src/com/ubuntuone/android/music/service/MusicServiceSupport.java 2013-01-25 16:20:29 +0000
@@ -56,6 +56,7 @@
56 private MusicService mMusicService;56 private MusicService mMusicService;
57 private BroadcastReceiver mHeadsetBroadcastReceiver;57 private BroadcastReceiver mHeadsetBroadcastReceiver;
58 private PhoneStateListener mPhoneStateListener;58 private PhoneStateListener mPhoneStateListener;
59 private boolean mWasOnCreateCalled = false;
59 60
60 public MusicServiceSupport(MusicService service) {61 public MusicServiceSupport(MusicService service) {
61 this.mMusicService = service;62 this.mMusicService = service;
@@ -64,6 +65,7 @@
64 }65 }
65 66
66 public void onCreate() {67 public void onCreate() {
68 mWasOnCreateCalled = true;
67 registerHeadsetBroadcastReceiver();69 registerHeadsetBroadcastReceiver();
68 registerMediaButtonBroadcastReceiver();70 registerMediaButtonBroadcastReceiver();
69 registerPhoneStateListener();71 registerPhoneStateListener();
@@ -79,6 +81,9 @@
79 }81 }
80 82
81 public void onDestroy() {83 public void onDestroy() {
84 if (!mWasOnCreateCalled) {
85 return;
86 }
82 unregisterHeadsetBroadcastReceiver();87 unregisterHeadsetBroadcastReceiver();
83 unregisterMediaButtonBroadcastReceiver();88 unregisterMediaButtonBroadcastReceiver();
84 unregisterPhoneStateListener();89 unregisterPhoneStateListener();
8590
=== modified file 'main/src/com/ubuntuone/android/music/ui/HomeActivity.java'
--- main/src/com/ubuntuone/android/music/ui/HomeActivity.java 2013-01-23 02:36:20 +0000
+++ main/src/com/ubuntuone/android/music/ui/HomeActivity.java 2013-01-25 16:20:29 +0000
@@ -419,7 +419,7 @@
419 }419 }
420 420
421 private boolean onSettingsOptionItemSelected() {421 private boolean onSettingsOptionItemSelected() {
422 Intent settingsIntent = new Intent(this, SettingsActivity.class);422 Intent settingsIntent = new Intent(this, PreferencesActivity.class);
423 startActivity(settingsIntent);423 startActivity(settingsIntent);
424 return true;424 return true;
425 }425 }
426426
=== added file 'main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java'
--- main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java 1970-01-01 00:00:00 +0000
+++ main/src/com/ubuntuone/android/music/ui/PreferencesActivity.java 2013-01-25 16:20:29 +0000
@@ -0,0 +1,269 @@
1/*
2 * Ubuntu One Music - stream music from Ubuntu One cloud storage.
3 *
4 * Copyright 2012-2013 Canonical Ltd.
5 *
6 * This file is part of Ubuntu One Music app.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see http://www.gnu.org/licenses
20 */
21
22package com.ubuntuone.android.music.ui;
23
24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
25import static com.ubuntuone.android.music.util.StorageUtils.MiB;
26
27import java.io.File;
28import java.lang.reflect.InvocationTargetException;
29import java.lang.reflect.Method;
30import java.util.Arrays;
31import java.util.List;
32
33import android.annotation.TargetApi;
34import android.content.ContentResolver;
35import android.content.ContentValues;
36import android.content.Context;
37import android.database.Cursor;
38import android.net.Uri;
39import android.os.AsyncTask;
40import android.os.Build;
41import android.os.Bundle;
42import android.preference.ListPreference;
43import android.preference.Preference;
44import android.preference.Preference.OnPreferenceChangeListener;
45import android.preference.Preference.OnPreferenceClickListener;
46import android.preference.PreferenceActivity;
47import android.preference.PreferenceFragment;
48import android.preference.PreferenceManager;
49import android.util.Log;
50import android.widget.Toast;
51
52import com.ubuntuone.android.music.Constants;
53import com.ubuntuone.android.music.R;
54import com.ubuntuone.android.music.provider.MusicContract.Songs;
55import com.ubuntuone.android.music.provider.MusicProviderUtils;
56import com.ubuntuone.android.music.util.SeekBarPreference;
57import com.ubuntuone.android.music.util.StorageUtils;
58import com.ubuntuone.android.music.util.StorageUtils.InvalidStorage;
59import com.ubuntuone.android.music.util.UIUtils;
60
61public class PreferencesActivity extends PreferenceActivity implements OnPreferenceClickListener {
62 private static final String TAG = makeLogTag(PreferencesActivity.class.getSimpleName());
63
64 protected Method mLoadHeaders = null;
65 protected Method mHasHeaders = null;
66 protected boolean mIsTablet;
67
68 protected ClearCacheTask mClearCacheTask;
69
70 public boolean hasHeadersCompat() {
71 if (mHasHeaders != null && mLoadHeaders != null) {
72 try {
73 return (Boolean) mHasHeaders.invoke(this);
74 } catch (IllegalArgumentException e) {
75 } catch (IllegalAccessException e) {
76 } catch (InvocationTargetException e) {
77 }
78 }
79 return false;
80 }
81
82 @SuppressWarnings("deprecation")
83 @Override
84 public void onCreate(Bundle aSavedState) {
85 // onBuildHeaders() will be called during super.onCreate()
86 try {
87 mLoadHeaders = getClass().getMethod("loadHeadersFromResource", int.class, List.class);
88 mHasHeaders = getClass().getMethod("hasHeaders");
89 } catch (NoSuchMethodException e) {
90 // We're handling an old API.
91 }
92 mIsTablet = UIUtils.isTablet(this);
93 super.onCreate(aSavedState);
94 if (!hasHeadersCompat() || !mIsTablet) {
95 addPreferencesFromResource(R.xml.preferences_account);
96 addPreferencesFromResource(R.xml.preferences_cache);
97 }
98
99 setupCacheLocationPreference();
100 setupCacheSizePreference(this);
101 setupClearCachePreference(this);
102 }
103
104 @Override
105 public void onBuildHeaders(List<Header> target) {
106 try {
107 if (mIsTablet) {
108 mLoadHeaders.invoke(this, new Object[] { R.xml.preference_headers, target });
109 }
110 } catch (IllegalArgumentException e) {
111 } catch (IllegalAccessException e) {
112 } catch (InvocationTargetException e) {
113 }
114 }
115
116 @TargetApi(Build.VERSION_CODES.HONEYCOMB)
117 static public class PreferencesFragment extends PreferenceFragment {
118 @Override
119 public void onCreate(Bundle savedInstanceState) {
120 super.onCreate(savedInstanceState);
121 Context context = getActivity().getApplicationContext();
122 int preferencesResource = context.getResources().getIdentifier(
123 getArguments().getString("preferenceResource"), "xml",
124 context.getPackageName());
125 PreferenceManager.setDefaultValues(context, preferencesResource, false);
126 addPreferencesFromResource(preferencesResource);
127 // Hack. On tablets, hide the category title duplicated in fragment title.
128 findPreference("preferenceCategory").setTitle("");
129 }
130 }
131
132 public void setupCacheLocationPreference() {
133 @SuppressWarnings("deprecation")
134 final ListPreference cacheLocation = (ListPreference) findPreference(
135 Constants.Preference.CACHE_LOCATION_KEY);
136 final String[] storagePaths = StorageUtils.getAvailableStoragePaths();
137 Log.i(TAG, "Available storage paths: " + Arrays.toString(storagePaths));
138 if (storagePaths != null) {
139 cacheLocation.setEntries(storagePaths);
140 cacheLocation.setEntryValues(storagePaths);
141
142 String storage = cacheLocation.getValue();
143 cacheLocation.setValue(storage);
144 cacheLocation.setSummary(storage);
145 try {
146 int index = StorageUtils.getSelectedStorageIndex(storage, storagePaths);
147 cacheLocation.setValueIndex(index);
148 } catch (InvalidStorage e) {
149 // Should not happen, as the value has been already set.
150 }
151
152 cacheLocation.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
153 @Override
154 public boolean onPreferenceChange(Preference preference, Object newValue) {
155 // TODO karni: Move cached music to new location or at least notify the user.
156 String location = (String) newValue;
157 cacheLocation.setSummary(location);
158 return true;
159 }
160 });
161 } else {
162 cacheLocation.setEnabled(false);
163 }
164 }
165
166 public void setupCacheSizePreference(Context context) {
167 @SuppressWarnings("deprecation")
168 SeekBarPreference seekBarPreference = (SeekBarPreference) findPreference(
169 Constants.Preference.CACHE_SIZE_KEY);
170 String storage = StorageUtils.getMusicDirectory(context);
171
172 long cacheSize = MusicProviderUtils.getCachedSongSize(context);
173 long min = Math.max(StorageUtils.MIN_CACHE_SIZE, cacheSize);
174 long max = StorageUtils.getAvailableStorage(context, storage) - cacheSize;
175
176 seekBarPreference.setMinValue((int)(min / MiB));
177 seekBarPreference.setMaxValue((int)(max / MiB));
178 seekBarPreference.setDefaultValue((int)(max / MiB));
179 }
180
181 public void setupClearCachePreference(Context context) {
182 @SuppressWarnings("deprecation")
183 Preference clearCachePreference = findPreference(Constants.Preference.CLEAR_CACHE_KEY);
184 long cacheSizeUsed = MusicProviderUtils.getCachedSongSize(context);
185 String fuzzySpaceUsed = StorageUtils.getFuzzySize(cacheSizeUsed);
186 Log.i(TAG, String.format("Cache stats: songs %d used %s",
187 MusicProviderUtils.getCachedSongCount(context), fuzzySpaceUsed));
188 clearCachePreference.setSummary(String.format("Space used: %s", fuzzySpaceUsed));
189 clearCachePreference.setOnPreferenceClickListener(this);
190 }
191
192 @Override
193 public boolean onPreferenceClick(Preference preference) {
194 String key = preference.getKey();
195 if (Constants.Preference.CLEAR_CACHE_KEY.equals(key)) {
196 if (mClearCacheTask == null) {
197 mClearCacheTask = new ClearCacheTask();
198 mClearCacheTask.execute();
199 }
200 }
201 return false;
202 }
203
204 public class ClearCacheTask extends AsyncTask<Integer, Void, Integer> {
205 private Context mContext = getApplicationContext();
206
207 @Override
208 protected void onPreExecute() {
209 Toast.makeText(mContext, "Clearing cache...", Toast.LENGTH_SHORT).show();
210 }
211
212 @Override
213 protected Integer doInBackground(Integer... params) {
214 ContentResolver resolver = mContext.getContentResolver();
215 String[] projection = new String[] { Songs.SONG_ID, Songs.SONG_PATH };
216 String selection = Songs.STARRED + "!=1";
217
218 Cursor cursor = null;
219 Integer deletedSongs = 0;
220 try {
221 cursor = resolver.query(Songs.CACHED_SONGS_URI, projection, selection, null, null);
222 if (cursor != null && cursor.moveToFirst()) {
223 int pathColumnIndex = cursor.getColumnIndex(Songs.SONG_PATH);
224 int songIdColumnIndex = cursor.getColumnIndex(Songs.SONG_ID);
225 do {
226 if (deleteSong(resolver, cursor, pathColumnIndex, songIdColumnIndex)) {
227 deletedSongs++;
228 }
229 } while (cursor.moveToNext());
230 }
231 } finally {
232 if (cursor != null) cursor.close();
233 }
234 return deletedSongs;
235 }
236
237 private boolean deleteSong(ContentResolver resolver, Cursor cursor,
238 int pathColumnIndex, int songIdColumnIndex) {
239 String path = cursor.getString(pathColumnIndex);
240 String songId = cursor.getString(songIdColumnIndex);
241 File songFile = new File(path);
242 if (songFile.exists()) {
243 Uri songUri = Songs.buildSongUri(songId);
244 ContentValues values = new ContentValues();
245 values.putNull(Songs.SONG_PATH);
246 boolean entryUpdated = resolver.update(songUri, values, null, null) == 1;
247 return songFile.delete() && entryUpdated;
248 }
249 return false;
250 }
251
252 @Override
253 protected void onPostExecute(Integer result) {
254 Toast.makeText(mContext, String.format("Deleted %d songs.", result),
255 Toast.LENGTH_SHORT).show();
256 mClearCacheTask = null;
257
258 if (!isFinishing()) {
259 setupClearCachePreference(PreferencesActivity.this);
260 }
261 }
262
263 @Override
264 protected void onCancelled() {
265 Toast.makeText(mContext, "Canceled.", Toast.LENGTH_SHORT).show();
266 mClearCacheTask = null;
267 }
268 }
269}
0270
=== removed file 'main/src/com/ubuntuone/android/music/ui/SettingsActivity.java'
--- main/src/com/ubuntuone/android/music/ui/SettingsActivity.java 2012-12-14 04:29:46 +0000
+++ main/src/com/ubuntuone/android/music/ui/SettingsActivity.java 1970-01-01 00:00:00 +0000
@@ -1,29 +0,0 @@
1/*
2 * Ubuntu One Music - stream music from Ubuntu One cloud storage.
3 *
4 * Copyright 2012 Canonical Ltd.
5 *
6 * This file is part of Ubuntu One Music app.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see http://www.gnu.org/licenses
20 */
21
22package com.ubuntuone.android.music.ui;
23
24import com.actionbarsherlock.app.SherlockPreferenceActivity;
25
26public class SettingsActivity extends SherlockPreferenceActivity
27{
28
29}
300
=== modified file 'main/src/com/ubuntuone/android/music/util/AccountUtils.java'
--- main/src/com/ubuntuone/android/music/util/AccountUtils.java 2013-01-23 02:36:20 +0000
+++ main/src/com/ubuntuone/android/music/util/AccountUtils.java 2013-01-25 16:20:29 +0000
@@ -92,11 +92,8 @@
92 if (accounts.length > 0) {92 if (accounts.length > 0) {
93 return U1_ACCOUNT_TYPE;93 return U1_ACCOUNT_TYPE;
94 }94 }
95 accounts = accountManager.getAccountsByType(OLD_U1_ACCOUNT_TYPE);95 // Otherwise, always return the old account type, regardless if the account exists.
96 if (accounts.length > 0) {96 return OLD_U1_ACCOUNT_TYPE;
97 return U1_ACCOUNT_TYPE;
98 }
99 return null;
100 }97 }
10198
102 public static String getChosenAccountName(final Context context) {99 public static String getChosenAccountName(final Context context) {
103100
=== added file 'main/src/com/ubuntuone/android/music/util/SeekBarPreference.java'
--- main/src/com/ubuntuone/android/music/util/SeekBarPreference.java 1970-01-01 00:00:00 +0000
+++ main/src/com/ubuntuone/android/music/util/SeekBarPreference.java 2013-01-25 16:20:29 +0000
@@ -0,0 +1,195 @@
1package com.ubuntuone.android.music.util;
2
3import static com.ubuntuone.android.music.util.StorageUtils.MiB;
4import android.content.Context;
5import android.content.res.TypedArray;
6import android.preference.Preference;
7import android.util.AttributeSet;
8import android.util.Log;
9import android.view.LayoutInflater;
10import android.view.View;
11import android.view.ViewGroup;
12import android.view.ViewParent;
13import android.widget.RelativeLayout;
14import android.widget.SeekBar;
15import android.widget.SeekBar.OnSeekBarChangeListener;
16import android.widget.TextView;
17
18import com.ubuntuone.android.music.R;
19
20public class SeekBarPreference extends Preference implements
21 OnSeekBarChangeListener {
22 private final String TAG = getClass().getName();
23
24 private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";
25 private static final String UBUNTUONE_NS = "http://schemas.one.ubuntu.com/apk/res/android";
26 private static final int DEFAULT_VALUE = 50;
27
28 private int mMaxValue = 100;
29 private int mMinValue = 0;
30 private int mInterval = 1;
31 private int mCurrentValue;
32 private SeekBar mSeekBar;
33
34 private TextView mStatusText;
35
36 public void setMinValue(int min) {
37 mMinValue = min;
38 mSeekBar.setMax(mMaxValue - mMinValue);
39 }
40
41 public void setMaxValue(int max) {
42 mMaxValue = max;
43 mSeekBar.setMax(mMaxValue - mMinValue);
44 }
45
46 public SeekBarPreference(Context context, AttributeSet attrs) {
47 super(context, attrs);
48 initPreference(context, attrs);
49 }
50
51 public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
52 super(context, attrs, defStyle);
53 initPreference(context, attrs);
54 }
55
56 private void initPreference(Context context, AttributeSet attrs) {
57 setValuesFromXml(attrs);
58 mSeekBar = new SeekBar(context, attrs);
59 mSeekBar.setMax(mMaxValue - mMinValue);
60 mSeekBar.setOnSeekBarChangeListener(this);
61 }
62
63 private void setValuesFromXml(AttributeSet attrs) {
64 mMaxValue = attrs.getAttributeIntValue(ANDROID_NS, "max", 100);
65 mMinValue = attrs.getAttributeIntValue(UBUNTUONE_NS, "min", 0);
66
67 try {
68 String newInterval = attrs.getAttributeValue(UBUNTUONE_NS, "interval");
69 if (newInterval != null) {
70 mInterval = Integer.parseInt(newInterval);
71 }
72 } catch (Exception e) {
73 Log.e(TAG, "Invalid interval value", e);
74 }
75
76 }
77
78 @SuppressWarnings("unused")
79 private String getAttributeStringValue(AttributeSet attrs,
80 String namespace, String name, String defaultValue) {
81 String value = attrs.getAttributeValue(namespace, name);
82 if (value == null) {
83 value = defaultValue;
84 }
85 return value;
86 }
87
88 @Override
89 protected View onCreateView(ViewGroup parent) {
90 RelativeLayout layout = null;
91 try {
92 LayoutInflater mInflater = (LayoutInflater) getContext()
93 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
94
95 layout = (RelativeLayout) mInflater.inflate(
96 R.layout.seek_bar_preference, parent, false);
97 } catch (Exception e) {
98 Log.e(TAG, "Error creating seek bar preference", e);
99 }
100 return layout;
101 }
102
103 @Override
104 public void onBindView(View view) {
105 super.onBindView(view);
106 try {
107 ViewParent oldContainer = mSeekBar.getParent();
108 ViewGroup newContainer = (ViewGroup) view
109 .findViewById(R.id.seekBarPrefBarContainer);
110
111 if (oldContainer != newContainer) {
112 if (oldContainer != null) {
113 ((ViewGroup) oldContainer).removeView(mSeekBar);
114 }
115 newContainer.removeAllViews();
116 newContainer.addView(mSeekBar,
117 ViewGroup.LayoutParams.MATCH_PARENT,
118 ViewGroup.LayoutParams.WRAP_CONTENT);
119 }
120 } catch (Exception ex) {
121 Log.e(TAG, "Error binding view: " + ex.toString());
122 }
123 updateView(view);
124 }
125
126 /**
127 * Update a SeekBarPreference view with our current state
128 *
129 * @param view
130 */
131 protected void updateView(View view) {
132
133 try {
134 RelativeLayout layout = (RelativeLayout) view;
135 mStatusText = (TextView) layout.findViewById(R.id.seekBarPrefValue);
136 mStatusText.setMinimumWidth(30);
137 mSeekBar.setProgress(mCurrentValue - mMinValue);
138
139 mStatusText.setText(StorageUtils.getFuzzySize(mCurrentValue * MiB));
140 } catch (Exception e) {
141 Log.e(TAG, "Error updating seek bar preference", e);
142 }
143
144 }
145
146 @Override
147 public void onProgressChanged(SeekBar seekBar, int progress,
148 boolean fromUser) {
149 int newValue = progress + mMinValue;
150
151 if (newValue > mMaxValue)
152 newValue = mMaxValue;
153 else if (newValue < mMinValue)
154 newValue = mMinValue;
155 else if (mInterval != 1 && newValue % mInterval != 0)
156 newValue = Math.round(((float) newValue) / mInterval) * mInterval;
157
158 mCurrentValue = newValue;
159 if (mStatusText != null) {
160 mStatusText.setText(StorageUtils.getFuzzySize(newValue * MiB));
161 }
162 persistInt(newValue);
163 }
164
165 @Override
166 public void onStartTrackingTouch(SeekBar seekBar) {
167 }
168
169 @Override
170 public void onStopTrackingTouch(SeekBar seekBar) {
171 notifyChanged();
172 }
173
174 @Override
175 protected Object onGetDefaultValue(TypedArray typedArray, int index) {
176 int defaultValue = typedArray.getInt(index, DEFAULT_VALUE);
177 return defaultValue;
178 }
179
180 @Override
181 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
182 if (restoreValue) {
183 mCurrentValue = getPersistedInt(mCurrentValue);
184 } else {
185 int value = 0;
186 try {
187 value = (Integer) defaultValue;
188 } catch (Exception ex) {
189 Log.e(TAG, "Invalid default value: " + defaultValue.toString());
190 }
191 persistInt(value);
192 mCurrentValue = value;
193 }
194 }
195}
0196
=== added file 'main/src/com/ubuntuone/android/music/util/StorageUtils.java'
--- main/src/com/ubuntuone/android/music/util/StorageUtils.java 1970-01-01 00:00:00 +0000
+++ main/src/com/ubuntuone/android/music/util/StorageUtils.java 2013-01-25 16:20:29 +0000
@@ -0,0 +1,216 @@
1/*
2 * Ubuntu One Music - stream music from Ubuntu One cloud storage.
3 *
4 * Copyright 2013 Canonical Ltd.
5 *
6 * This file is part of Ubuntu One Music app.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see http://www.gnu.org/licenses
20 */
21
22package com.ubuntuone.android.music.util;
23
24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
25
26import java.io.File;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Locale;
30
31import android.annotation.SuppressLint;
32import android.content.Context;
33import android.content.SharedPreferences;
34import android.os.Build;
35import android.os.Environment;
36import android.os.StatFs;
37import android.preference.PreferenceManager;
38import android.util.Log;
39
40import com.ubuntuone.android.music.Constants;
41import com.ubuntuone.android.music.Constants.Preference;
42
43/** Utility functions to query and store information about the SDCard "external storage". */
44public final class StorageUtils {
45 private static final String TAG = makeLogTag(StorageUtils.class.getSimpleName());
46
47 public static final String MUSIC_DIR = "u1music";
48
49 public static final long MiB = 1024*1024;
50 public static final long MIN_CACHE_SIZE = 100 * MiB;
51
52 private StorageUtils() {}
53
54 public static void setupMusicDirectory(Context context) {
55 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
56 String storage = preferences.getString(Preference.CACHE_LOCATION_KEY, null);
57 if (storage == null) {
58 final String[] storagePaths = StorageUtils.getAvailableStoragePaths();
59 Log.i(TAG, "Available storage paths: " + Arrays.toString(storagePaths));
60 if (storagePaths != null) {
61 storage = storagePaths[0];
62 } else {
63 storage = getGenericCacheDirectory();
64 }
65 PreferenceManager.getDefaultSharedPreferences(context).edit()
66 .putString(Constants.Preference.CACHE_LOCATION_KEY, storage).commit();
67
68 } else {
69 Log.w(TAG, "No storage paths matched on " + Build.MODEL);
70 }
71
72 long avail = getAvailableStorage(context, storage);
73 long total = getTotalStorage(context, storage);
74 Log.i(TAG, String.format("Storage stats: avail %s total %s",
75 getFuzzySize(avail), getFuzzySize(total)));
76 }
77
78 /**
79 * Returns the directory used to store cached music collection located on the selected storage.
80 * <br />
81 * Prerequisite: <b>setupMusicDirectory</b> must be called before calling this method,
82 * to initialize the default value of cache location preference.
83 *
84 * @param context
85 *
86 * @return Absolute path of the music storage directory.
87 */
88 public static String getMusicDirectory(Context context) {
89 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
90 String cacheStorage = preferences.getString(Preference.CACHE_LOCATION_KEY, null);
91 String cacheDirectory = preferences.getString(Preference.CACHE_DIR_KEY, MUSIC_DIR);
92 if (cacheStorage == null) {
93 throw new IllegalStateException("storage is null, have you called setupMusicDirectory?");
94 }
95 File storageDirectory = new File(cacheStorage);
96 File musicDirectory = new File(storageDirectory, cacheDirectory);
97 if (!musicDirectory.exists()) {
98 musicDirectory.mkdirs();
99 }
100 return musicDirectory.getAbsolutePath();
101 }
102
103 /**
104 * @return Absolute path of the default, generic music storage directory.
105 */
106 public static String getGenericCacheDirectory() {
107 final File storageDirectory = Environment.getExternalStorageDirectory();
108 final File genericCache = new File(storageDirectory,
109 "Android/data/com.ubuntuone.android.music/files/cache");
110 if (!genericCache.isDirectory()) {
111 Log.i(TAG, "Creating cache directory.");
112 genericCache.delete(); // Doesn't throw IOException
113 genericCache.mkdirs();
114 }
115 return genericCache.getAbsolutePath();
116 }
117
118 public static String getExternalStoragePath() {
119 return Environment.getExternalStorageDirectory().getAbsolutePath();
120 }
121
122 @SuppressLint("SdCardPath") // Yada yada lint. getExternalStorageDirectory() is NOT enough.
123 private static final String[] knownStoragePaths = new String[] {
124 "/storage/sdcard0",
125 "/mnt/sdcard",
126 "/mnt/sdcard2",
127 "/mnt/sdcard-ext", // Motorola
128 "/mnt/sdcard/external_sd", // Samsung
129 "/mnt/ext_card", // Samsung
130 "/sdcard/_ExternalSD"
131 };
132
133 public static String[] getAvailableStoragePaths() {
134 ArrayList<String> pathList = new ArrayList<String>();
135 for (String path : knownStoragePaths) {
136 File storage = new File(path);
137 if (storage.exists() && storage.isDirectory()) {
138 pathList.add(storage.getAbsolutePath());
139 }
140 }
141 int size = pathList.size();
142 return pathList.toArray(new String[size]);
143 }
144
145 public static int getSelectedStorageIndex(String storage,
146 String[] storagePaths) throws InvalidStorage {
147 for (int i = 0; i < storagePaths.length; i++) {
148 if (storage.equals(storagePaths[i]))
149 return i;
150 }
151 throw new InvalidStorage();
152 }
153
154 public static long getAvailableStorage(Context context, String storage) {
155 StatFs stat = new StatFs(getMusicDirectory(context));
156 long blocksAvailable = stat.getAvailableBlocks();
157 long blockSizeBytes = stat.getBlockSize();
158 return blocksAvailable * blockSizeBytes;
159 }
160
161 /**
162 * total space - free space - used cache size
163 *
164 * @param context
165 * @param storage
166 * @return
167 */
168 public static long getAvailableCacheStorage(Context context, String storage, long cacheSize) {
169 StatFs stat = new StatFs(getMusicDirectory(context));
170 long blocksAvailable = stat.getAvailableBlocks();
171 long blockSizeBytes = stat.getBlockSize();
172 long total = getTotalStorage(context, storage);
173
174 long avail = blocksAvailable * blockSizeBytes;
175 return total - avail - cacheSize;
176 }
177
178 public static long getTotalStorage(Context context, String storage) {
179 StatFs stat = new StatFs(getMusicDirectory(context));
180 long blocksCount = stat.getBlockCount();
181 long blockSizeBytes = stat.getBlockSize();
182 return blocksCount * blockSizeBytes;
183 }
184
185 public static String getFuzzySize(long size) {
186 String units = "B";
187 String formatString = "%.0f %s";
188 double s = size;
189 if (s > 1024.0) {
190 s /= 1024.0;
191 units = "KiB";
192 }
193 if (s > 1024.0) {
194 s /= 1024.0;
195 units = "MiB";
196 formatString = "%.02f %s";
197 }
198 if (s > 1024.0) {
199 s /= 1024.0;
200 units = "GiB";
201 }
202 return String.format(Locale.US, formatString, s, units);
203 }
204
205 public static class InvalidStorage extends Exception {
206 private static final long serialVersionUID = -3042684172473773638L;
207 public InvalidStorage() { super(); }
208 public InvalidStorage(String msg) { super(msg); }
209 }
210
211 public static class NoTertiaryStorage extends Exception {
212 private static final long serialVersionUID = -5605241960717201218L;
213 public NoTertiaryStorage() { super(); }
214 public NoTertiaryStorage(String msg) { super(msg); }
215 }
216}

Subscribers

People subscribed via source and target branches

to all changes: