diff options
author | Martin Brabham <optedoblivion@cyngn.com> | 2014-12-15 10:28:45 -0800 |
---|---|---|
committer | Martin Brabham <optedoblivion@cyngn.com> | 2014-12-16 11:51:44 -0800 |
commit | 486c19adee31fac87ef5a70fe37611e64aea99d1 (patch) | |
tree | 73d0139b57d74204b0673288113750563ac09e8a | |
parent | 3a587806258e5d39f4dcaea2220a3b5bd590b0d4 (diff) | |
download | android_packages_apps_CMFileManager-486c19adee31fac87ef5a70fe37611e64aea99d1.tar.gz android_packages_apps_CMFileManager-486c19adee31fac87ef5a70fe37611e64aea99d1.tar.bz2 android_packages_apps_CMFileManager-486c19adee31fac87ef5a70fe37611e64aea99d1.zip |
CMFileManager: add usage stats by mime type
Change-Id: I92ffb45ec3ef0dc6feb8b8129bcb0fedf442ba54
-rw-r--r-- | AndroidManifest.xml | 12 | ||||
-rw-r--r-- | res/layout/disk_usage_category_view.xml | 38 | ||||
-rw-r--r-- | res/layout/filesystem_info_dialog.xml | 15 | ||||
-rw-r--r-- | res/values/colors.xml | 26 | ||||
-rw-r--r-- | res/values/dimen.xml | 6 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/FileManagerApplication.java | 9 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/model/DiskUsage.java | 44 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/model/DiskUsageCategory.java | 72 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/providers/MimeTypeIndexProvider.java | 221 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/service/MimeTypeIndexService.java | 165 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/tasks/FetchStatsByTypeTask.java | 78 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/ui/dialogs/FilesystemInfoDialog.java | 117 | ||||
-rw-r--r-- | src/com/cyanogenmod/filemanager/ui/widgets/DiskUsageGraph.java | 343 |
13 files changed, 1020 insertions, 126 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d442b2a7..88df3cd0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -62,6 +62,18 @@ android:grantUriPermissions="true" android:exported="true" /> + <provider + android:authorities="com.cyanogenmod.filemanager.providers.index" + android:name=".providers.MimeTypeIndexProvider"/> + + <service + android:name=".service.MimeTypeIndexService" + android:label="@string/app_name"> + <intent-filter> + <action android:name="com.cyanogenmod.filemanager.ACTION_START_INDEX"/> + </intent-filter> + </service> + <activity android:name=".activities.NavigationActivity" android:label="@string/app_name" diff --git a/res/layout/disk_usage_category_view.xml b/res/layout/disk_usage_category_view.xml new file mode 100644 index 00000000..5b4c5c43 --- /dev/null +++ b/res/layout/disk_usage_category_view.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:weightSum="1.0" + android:gravity="center_vertical"> + + <View + android:id="@+id/v_legend_swatch" + android:layout_width="10dp" + android:layout_height="10dp" + android:layout_weight=".2"/> + + <TextView + android:id="@+id/tv_legend_title" + android:textSize="@dimen/legend_title_textSize" + android:paddingLeft="@dimen/legend_title_paddingLeft" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight=".8"/> + +</LinearLayout> diff --git a/res/layout/filesystem_info_dialog.xml b/res/layout/filesystem_info_dialog.xml index 4b51232b..07303394 100644 --- a/res/layout/filesystem_info_dialog.xml +++ b/res/layout/filesystem_info_dialog.xml @@ -351,4 +351,19 @@ </TableLayout> </LinearLayout> + <GridLayout + android:id="@+id/ll_legend" + android:gravity="center" + android:layout_centerHorizontal="true" + android:visibility="invisible" + android:layout_below="@id/filesystem_tab_diskusage" + android:orientation="horizontal" + android:columnCount="4" + android:columnOrderPreserved="true" + android:rowOrderPreserved="true" + android:useDefaultMargins="true" + android:padding="@dimen/legend_padding" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </RelativeLayout> diff --git a/res/values/colors.xml b/res/values/colors.xml index 09d84d50..123860c5 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -65,4 +65,30 @@ <color name="material_palette_blue_primary">#1E88E5</color> <color name="material_palette_blue_primary_dark">#1976D2</color> <color name="actionbar_palette_blue_primary">#1E88E5</color> + + + <!-- Disk usage blue --> + <color name="material_palette_blue_1">#64B5F6</color> + <color name="material_palette_blue_2">#2196F3</color> + <color name="material_palette_blue_3">#1976D2</color> + <color name="material_palette_blue_4">#0D47A1</color> + + <!-- Disk usage green --> + <color name="material_palette_green_1">#DCE775</color> + <color name="material_palette_green_2">#CDDC39</color> + <color name="material_palette_green_3">#AFB42B</color> + <color name="material_palette_green_4">#827717</color> + + <!-- Disk usage orange --> + <color name="material_palette_orange_1">#FFB74D</color> + <color name="material_palette_orange_2">#FF9800</color> + <color name="material_palette_orange_3">#F57C00</color> + <color name="material_palette_orange_4">#E65100</color> + + <!-- Disk usage pink --> + <color name="material_palette_pink_1">#F06292</color> + <color name="material_palette_pink_2">#E91E63</color> + <color name="material_palette_pink_3">#C2185B</color> + <color name="material_palette_pink_4">#880E4F</color> + </resources> diff --git a/res/values/dimen.xml b/res/values/dimen.xml index 5bd2fc14..58307cb7 100644 --- a/res/values/dimen.xml +++ b/res/values/dimen.xml @@ -124,4 +124,10 @@ <dimen name="scrim_layout_elevation">10dp</dimen> <!-- Drawer header height --> <dimen name="drawer_header_height">150dp</dimen> + + <!-- usage graph stuff --> + <dimen name="legend_title_textSize">9sp</dimen> + <dimen name="legend_title_paddingLeft">3dp</dimen> + <dimen name="legend_padding">10dp</dimen> + </resources> diff --git a/src/com/cyanogenmod/filemanager/FileManagerApplication.java b/src/com/cyanogenmod/filemanager/FileManagerApplication.java index ebf369a1..0529b97b 100644 --- a/src/com/cyanogenmod/filemanager/FileManagerApplication.java +++ b/src/com/cyanogenmod/filemanager/FileManagerApplication.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.os.Environment; import android.util.Log; import com.cyanogenmod.filemanager.console.Console; @@ -34,6 +35,7 @@ import com.cyanogenmod.filemanager.preferences.AccessMode; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier; import com.cyanogenmod.filemanager.preferences.Preferences; +import com.cyanogenmod.filemanager.service.MimeTypeIndexService; import com.cyanogenmod.filemanager.ui.ThemeManager; import com.cyanogenmod.filemanager.ui.ThemeManager.Theme; import com.cyanogenmod.filemanager.util.AIDHelper; @@ -174,6 +176,13 @@ public final class FileManagerApplication extends Application { } init(); register(); + + // Kick off usage by mime type indexing for external storage; most likely use case for + // file manager + File externalStorage = Environment.getExternalStorageDirectory(); + MimeTypeIndexService.indexFileRoot(this, externalStorage.getAbsolutePath()); + MimeTypeIndexService.indexFileRoot(this, "/system"); + } /** diff --git a/src/com/cyanogenmod/filemanager/model/DiskUsage.java b/src/com/cyanogenmod/filemanager/model/DiskUsage.java index 693e39b0..5ce49bf4 100644 --- a/src/com/cyanogenmod/filemanager/model/DiskUsage.java +++ b/src/com/cyanogenmod/filemanager/model/DiskUsage.java @@ -17,6 +17,8 @@ package com.cyanogenmod.filemanager.model; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * A class that holds information about the usage of a disk (total, used and free space). @@ -25,6 +27,8 @@ public class DiskUsage implements Serializable { private static final long serialVersionUID = -4540446701543226294L; + private final List<DiskUsageCategory> mDiskUsageCategoryList = + new ArrayList<DiskUsageCategory>(); private final String mMountPoint; private final long mTotal; private final long mUsed; @@ -83,6 +87,44 @@ public class DiskUsage implements Serializable { } /** + * Method that returns the total sum of all categories + * + * @return {@link java.lang.Long} + */ + public long getCategorySum() { + long bytes = 0; + for (DiskUsageCategory category : getUsageCategoryList()) { + bytes += category.getSizeBytes(); + } + return bytes; + } + + /** + * Add a usage category + * + * @param category {@link com.cyanogenmod.filemanager.model.DiskUsageCategory} not null + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public void addUsageCategory(DiskUsageCategory category) throws IllegalArgumentException { + if (category == null) { + throw new IllegalArgumentException("'category' cannot be null!"); + } + mDiskUsageCategoryList.add(category); + } + + public List<DiskUsageCategory> getUsageCategoryList() { + return mDiskUsageCategoryList; + } + + /** + * Clears the list of usage categories + */ + public void clearUsageCategories() { + mDiskUsageCategoryList.clear(); + } + + /** * {@inheritDoc} */ @Override @@ -141,6 +183,4 @@ public class DiskUsage implements Serializable { + this.mFree + "]"; //$NON-NLS-1$ } - - } diff --git a/src/com/cyanogenmod/filemanager/model/DiskUsageCategory.java b/src/com/cyanogenmod/filemanager/model/DiskUsageCategory.java new file mode 100644 index 00000000..e1be6a03 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/model/DiskUsageCategory.java @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2014 The CyanogenMod Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.cyanogenmod.filemanager.model; + +import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory; + +/** + * DiskUsageCategory + * <pre> + * Category by mime type and the amount of bytes it is using + * </pre> + */ +public class DiskUsageCategory { + + // Members + private MimeTypeCategory mCategory; + private long mSizeBytes = 0l; + + /** + * Simple constructor + */ + public DiskUsageCategory() { + } + + /** + * Constructor + * + * @param category {@link com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory} + * @param sizeBytes {@link java.lang.Long} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public DiskUsageCategory(MimeTypeCategory category, long sizeBytes) + throws IllegalArgumentException { + if (category == null) { + throw new IllegalArgumentException("'category' may not be null!"); + } + mCategory = category; + mSizeBytes = sizeBytes; + } + + public MimeTypeCategory getCategory() { + return mCategory; + } + + public void setCategory(MimeTypeCategory category) { + mCategory = category; + } + + public long getSizeBytes() { + return mSizeBytes; + } + + public void setSizeBytes(long sizeBytes) { + mSizeBytes = sizeBytes; + } + +} diff --git a/src/com/cyanogenmod/filemanager/providers/MimeTypeIndexProvider.java b/src/com/cyanogenmod/filemanager/providers/MimeTypeIndexProvider.java new file mode 100644 index 00000000..6945bda3 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/providers/MimeTypeIndexProvider.java @@ -0,0 +1,221 @@ +/* +* Copyright (C) 2014 The CyanogenMod Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.cyanogenmod.filemanager.providers; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import static android.content.UriMatcher.NO_MATCH; + +/** + * MimeTypeIndexProvider + * <pre> + * Provider for handling access of mime type indexes + * </pre> + * + * @see {@link android.content.ContentProvider} + */ +public class MimeTypeIndexProvider extends ContentProvider { + + // Constants + private static final String TAG = MimeTypeIndexProvider.class.getSimpleName(); + private static final String AUTHORITY = "com.cyanogenmod.filemanager.providers.index"; + private static final int ID_INDEX = 1; + private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + + DatabaseHelper.INDEX_TABLE); + + private static final UriMatcher sUriMatcher = new UriMatcher(NO_MATCH); + public static final String COLUMN_FILE_ROOT = "file_root"; + public static final String COLUMN_CATEGORY = "category"; + public static final String COLUMN_SIZE = "size"; + + public static Uri getContentUri() { + return new Uri.Builder().scheme("content").authority(AUTHORITY).path + (DatabaseHelper.INDEX_TABLE).build(); + } + + static { + sUriMatcher.addURI(AUTHORITY, DatabaseHelper.INDEX_TABLE, ID_INDEX); + } + + private SQLiteDatabase mSQLiteDatabase; + + @Override + public boolean onCreate() { + DatabaseHelper dbHelper = new DatabaseHelper(getContext()); + mSQLiteDatabase = dbHelper.getWritableDatabase(); + return mSQLiteDatabase != null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + String tableName = null; + switch (sUriMatcher.match(uri)) { + case ID_INDEX: + tableName = DatabaseHelper.INDEX_TABLE; + break; + default: + throw new RuntimeException("URI not supported!"); + } + + Cursor cursor = mSQLiteDatabase.query(tableName, projection, selection, selectionArgs, null, null, sortOrder); + if (cursor != null) { + cursor.setNotificationUri(getContext().getContentResolver(), uri); + } + return cursor; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues contentValues) { + String tableName = null; + switch (sUriMatcher.match(uri)) { + case ID_INDEX: + tableName = DatabaseHelper.INDEX_TABLE; + break; + default: + throw new RuntimeException("URI not supported!"); + } + long rowId = mSQLiteDatabase.insert(tableName, null, contentValues); + if (rowId > 0) { + Uri newUri = ContentUris.withAppendedId(CONTENT_URI, rowId); + getContext().getContentResolver().notifyChange(newUri, null); + return newUri; + } + throw new SQLException("Failed to add new record"); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + String tableName = null; + switch (sUriMatcher.match(uri)) { + case ID_INDEX: + tableName = DatabaseHelper.INDEX_TABLE; + break; + default: + throw new RuntimeException("URI not supported!"); + } + int count = mSQLiteDatabase.delete(tableName, selection, selectionArgs); + getContext().getContentResolver().notifyChange(uri, null); + return count; + } + + @Override + public int update(Uri uri, ContentValues contentValues, String selection, String[] + selectionArgs) { + throw new RuntimeException("MimeTypeIndexProvider::update(): Not implemented!"); + } + + private static class DatabaseHelper extends SQLiteOpenHelper { + + // Constants + static final int DATABASE_VERSION = 1; + static final String DATABASE_NAME = "mime_type_index"; + static final String INDEX_TABLE = "type_index"; + static final String CREATE_INDEX_TABLE = "CREATE TABLE IF NOT EXISTS " + + INDEX_TABLE + + " ( " + + "`_id` INTEGER PRIMARY KEY AUTOINCREMENT, " + + "`" + COLUMN_FILE_ROOT + "` TEXT, " + + "`" + COLUMN_CATEGORY + "` TEXT, " + + "`" + COLUMN_SIZE + "` INTEGER " + + ")"; + + /** + * Constructor + * + * @param context {@link android.content.Context} + */ + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase sqLiteDatabase) { + sqLiteDatabase.execSQL(CREATE_INDEX_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { + sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + INDEX_TABLE); + onCreate(sqLiteDatabase); + } + } + + /** + * Get the mount point usage data via sql cursor + * + * @param context {@link android.content.Context} not null + * @param fileRoot {@link java.lang.String} not null or empty + * + * @return {@link android.database.Cursor} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public static Cursor getMountPointUsage(Context context, String fileRoot) throws + IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null!"); + } + if (TextUtils.isEmpty(fileRoot)) { + throw new IllegalArgumentException("'fileRoot' cannot be null or empty!"); + } + String selection = COLUMN_FILE_ROOT + " = ?"; + String[] selectionArgs = new String[] { fileRoot }; + return context.getContentResolver().query(MimeTypeIndexProvider.getContentUri(), + null, selection, selectionArgs, null); + } + + /** + * Clear the mount point usage data for the file root + * + * @param context {@link android.content.Context} not null + * @param fileRoot {@link java.lang.String} not null or empty + * + * @return {@link java.lang.Integer} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public static int clearMountPointUsages(Context context, String fileRoot) throws + IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null!"); + } + if (TextUtils.isEmpty(fileRoot)) { + throw new IllegalArgumentException("'fileRoot' cannot be null or empty!"); + } + String selection = COLUMN_FILE_ROOT + " = ?"; + String[] selectionArgs = new String[] { fileRoot }; + return context.getContentResolver().delete(MimeTypeIndexProvider.getContentUri(), selection, selectionArgs); + } + +} diff --git a/src/com/cyanogenmod/filemanager/service/MimeTypeIndexService.java b/src/com/cyanogenmod/filemanager/service/MimeTypeIndexService.java new file mode 100644 index 00000000..7f926920 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/service/MimeTypeIndexService.java @@ -0,0 +1,165 @@ +/* +* Copyright (C) 2014 The CyanogenMod Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.cyanogenmod.filemanager.service; + +import android.app.IntentService; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.util.Log; +import com.cyanogenmod.filemanager.providers.MimeTypeIndexProvider; +import com.cyanogenmod.filemanager.util.MimeTypeHelper; +import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory; + +import java.io.File; +import java.io.FileFilter; +import java.util.HashMap; +import java.util.Map; + +/** + * MimeTypeIndexService + * <pre> + * Service intended to index space used by mime type + * </pre> + * + * @see {@link android.app.IntentService} + */ +public class MimeTypeIndexService extends IntentService { + + // Constants + private static final String TAG = MimeTypeIndexService.class.getSimpleName(); + public static final String ACTION_START_INDEX = "com.cyanogenmod.filemanager" + + ".ACTION_START_INDEX"; + public static final String EXTRA_FILE_ROOT = "extra_file_root"; + + /** + * Constructor + */ + public MimeTypeIndexService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.v(TAG, "onHandleIntent(" + intent + ")"); + if (intent == null) { + Log.w(TAG, "Intent passed was null"); + return; + } + String action = intent.getAction(); + Log.d(TAG, "Action: " + action); + if (TextUtils.isEmpty(action)) { + Log.w(TAG, "Failed to parse action"); + return; + } + String fileRoot = intent.getStringExtra(EXTRA_FILE_ROOT); + if (TextUtils.isEmpty(fileRoot)) { + Log.w(TAG, "Empty file root, bailing out"); + return; + } + if (ACTION_START_INDEX.equalsIgnoreCase(action)) { + performIndexAction(fileRoot); + } + } + + private void performIndexAction(String fileRoot) { + Log.v(TAG, "performIndexAction(" + fileRoot + ")"); + if (TextUtils.isEmpty(fileRoot)) { + Log.w(TAG, "Empty or null file root '" + fileRoot + "'"); + return; + } + Log.i(TAG, "Starting mime type usage indexing on '" + fileRoot + "'"); + fileRoot = fileRoot.trim(); + File rootFile = new File(fileRoot); + Map<MimeTypeCategory, Long> spaceCalculationMap = + new HashMap<MimeTypeCategory, Long>(); + calculateUsageByType(rootFile, spaceCalculationMap); + ContentValues[] valuesList = new ContentValues[spaceCalculationMap.keySet().size()]; + int i = 0; + for (MimeTypeCategory category : spaceCalculationMap.keySet()) { + Log.d(TAG, "" + category + " = " + spaceCalculationMap.get(category)); + ContentValues values = new ContentValues(); + values.put(MimeTypeIndexProvider.COLUMN_FILE_ROOT, fileRoot); + values.put(MimeTypeIndexProvider.COLUMN_CATEGORY, category.name()); + values.put(MimeTypeIndexProvider.COLUMN_SIZE, spaceCalculationMap.get(category)); + valuesList[i] = values; + i++; + } + MimeTypeIndexProvider.clearMountPointUsages(this, fileRoot); // Clear old data + getContentResolver().bulkInsert(MimeTypeIndexProvider.getContentUri(), valuesList); + } + + private class FileOnlyFileFilter implements FileFilter { + @Override + public boolean accept(File file) { + return file != null && !file.isDirectory() && file.isFile(); + } + } + + private class DirectoryOnlyFileFilter implements FileFilter { + @Override + public boolean accept(File file) { + return file != null && file.isDirectory(); + } + } + + private void calculateUsageByType(File root, Map<MimeTypeCategory, Long> groupUsageMap) { + File[] dirs = root.listFiles(new DirectoryOnlyFileFilter()); + File[] files = root.listFiles(new FileOnlyFileFilter()); + if (dirs != null) { + // Recurse directories + for (File dir : dirs) { + calculateUsageByType(dir, groupUsageMap); + } + } + if (files != null) { + // Iterate every file + for (File file : files) { + MimeTypeCategory category = MimeTypeHelper.getCategory(this, file); + long size = file.length(); + if (!groupUsageMap.containsKey(category)) { + groupUsageMap.put(category, size); + } else { + long newSum = groupUsageMap.get(category) + size; + groupUsageMap.put(category, newSum); + } + } + } + } + + /** + * Kick off an indexing job for the provided file root or mount point root + * + * @param context {@link android.content.Context} + * @param fileRoot {@link java.lang.String} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public static void indexFileRoot(Context context, String fileRoot) throws + IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null"); + } + // Start indexing the external storage + Intent intent = new Intent(context, MimeTypeIndexService.class); + intent.setAction(MimeTypeIndexService.ACTION_START_INDEX); + intent.putExtra(MimeTypeIndexService.EXTRA_FILE_ROOT, fileRoot); + context.startService(intent); + } + +} diff --git a/src/com/cyanogenmod/filemanager/tasks/FetchStatsByTypeTask.java b/src/com/cyanogenmod/filemanager/tasks/FetchStatsByTypeTask.java new file mode 100644 index 00000000..de9cfc6c --- /dev/null +++ b/src/com/cyanogenmod/filemanager/tasks/FetchStatsByTypeTask.java @@ -0,0 +1,78 @@ +/* +* Copyright (C) 2014 The CyanogenMod Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.cyanogenmod.filemanager.tasks; + +import android.content.Context; +import android.database.Cursor; +import android.os.AsyncTask; +import com.cyanogenmod.filemanager.providers.MimeTypeIndexProvider; + +/** + * FetchStatsByTypeTask + * <pre> + * Task for fetching the cursor of data for stats by mime type + * </pre> + * + * @see {@link android.os.AsyncTask} + */ +public class FetchStatsByTypeTask extends AsyncTask<String, Void, Cursor> { + + // Members + private Context mContext; + private Listener mListener; + + /** + * Constructor + * + * @param context {@link android.content.Context} + * @param listener {@link com.cyanogenmod.filemanager.tasks.FetchStatsByTypeTask.Listener} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public FetchStatsByTypeTask(Context context, Listener listener) + throws IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null"); + } + mContext = context; + mListener = listener; + } + + @Override + protected Cursor doInBackground(String... strings) { + if (strings.length < 1) { + return null; + } + String fileRoot = strings[0]; + return MimeTypeIndexProvider.getMountPointUsage(mContext, fileRoot); + } + + @Override + protected void onPostExecute(Cursor cursor) { + if (mListener != null) { + mListener.onCursor(cursor); + } + } + + /** + * Callback interface for this task + */ + public interface Listener { + public void onCursor(Cursor cursor); + } + +} diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/FilesystemInfoDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/FilesystemInfoDialog.java index 392f36fd..5ead3192 100644 --- a/src/com/cyanogenmod/filemanager/ui/dialogs/FilesystemInfoDialog.java +++ b/src/com/cyanogenmod/filemanager/ui/dialogs/FilesystemInfoDialog.java @@ -19,37 +19,128 @@ package com.cyanogenmod.filemanager.ui.dialogs; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; +import android.database.Cursor; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.GridLayout; import android.widget.Switch; import android.widget.TextView; - import com.cyanogenmod.filemanager.FileManagerApplication; import com.cyanogenmod.filemanager.R; import com.cyanogenmod.filemanager.console.Console; import com.cyanogenmod.filemanager.console.ConsoleBuilder; import com.cyanogenmod.filemanager.model.DiskUsage; +import com.cyanogenmod.filemanager.model.DiskUsageCategory; import com.cyanogenmod.filemanager.model.MountPoint; import com.cyanogenmod.filemanager.preferences.AccessMode; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; import com.cyanogenmod.filemanager.preferences.Preferences; +import com.cyanogenmod.filemanager.providers.MimeTypeIndexProvider; +import com.cyanogenmod.filemanager.tasks.FetchStatsByTypeTask; +import com.cyanogenmod.filemanager.tasks.FetchStatsByTypeTask.Listener; import com.cyanogenmod.filemanager.ui.ThemeManager; import com.cyanogenmod.filemanager.ui.ThemeManager.Theme; import com.cyanogenmod.filemanager.ui.widgets.DiskUsageGraph; import com.cyanogenmod.filemanager.util.CommandHelper; import com.cyanogenmod.filemanager.util.DialogHelper; import com.cyanogenmod.filemanager.util.FileHelper; +import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory; import com.cyanogenmod.filemanager.util.MountPointHelper; /** * A class that wraps a dialog for showing information about a mount point.<br /> * This class display information like mount point name, device name, size, type, ... */ -public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeListener { +public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeListener, Listener { + + @Override + public void onCursor(Cursor cursor) { + if (cursor == null) { + Log.w(TAG, "Cursor is null"); + return; + } + if (mDiskUsage == null) { + Log.w(TAG, "No disk usage available!"); + return; + } + mDiskUsage.clearUsageCategories(); + while(cursor.moveToNext()) { + String fileRoot = cursor.getString(cursor.getColumnIndex(MimeTypeIndexProvider + .COLUMN_FILE_ROOT)); + String categoryString = cursor.getString(cursor.getColumnIndex(MimeTypeIndexProvider + .COLUMN_CATEGORY)); + long size = cursor.getLong(cursor.getColumnIndex(MimeTypeIndexProvider.COLUMN_SIZE)); + MimeTypeCategory category = MimeTypeCategory.valueOf(categoryString); + DiskUsageCategory usageCategory = new DiskUsageCategory(category, size); + mDiskUsage.addUsageCategory(usageCategory); + + // [TODO][MSB]: Unhandled case: No data, sync in progress, ready to draw + // * Should check if 0 length, then wait on uri notification? + // ** This should only happen if you are using it without a sync ever having happened + // ** before. + // * Should always wait on uri notification and update drawing? + // * Otherwise if we have data then we are good to go! + // * Also should think of a way to dispatch and index refresh (alarm manager? file + // ** observer?) + + } + + this.mDiskUsageGraph.post(new Runnable() { + @Override + public void run() { + //Animate disk usage graph + FilesystemInfoDialog.this.mDiskUsageGraph.drawDiskUsage(mDiskUsage); + if (mIsInUsageTab) { + if (mLegendLayout.getVisibility() != View.VISIBLE) { + populateLegend(); + mLegendLayout.setVisibility(View.VISIBLE); + } + } + isFetching = false; + } + }); + } + + private void populateLegend() { + if (mLegendLayout == null) { + Log.w(TAG, "Unable to find view for legend"); + return; + } + mLegendLayout.removeAllViews(); + LayoutInflater inflater = LayoutInflater.from(mContext); + int index = 0; + if (mDiskUsage == null) { + Log.w(TAG, "No disk usage information"); + return; + } + for (DiskUsageCategory category : mDiskUsage.getUsageCategoryList()) { + View ll = inflater.inflate(R.layout.disk_usage_category_view, null, false); + View colorView = ll.findViewById(R.id.v_legend_swatch); + index = (index < DiskUsageGraph.COLOR_LIST.size()) ? index : 0; // normalize index + colorView.setBackgroundColor(DiskUsageGraph.COLOR_LIST.get(index)); + TextView titleView = (TextView) ll.findViewById(R.id.tv_legend_title); + String localizedName = MimeTypeCategory.getFriendlyLocalizedNames(mContext)[category + .getCategory().ordinal()]; + titleView.setText(localizedName); + mLegendLayout.addView(ll); + index++; + } + } + + boolean isFetching; + FetchStatsByTypeTask mFetchStatsByTypeTask; + private void fetchStats(String fileRoot) { + if (!isFetching) { + isFetching = true; + mFetchStatsByTypeTask.execute(fileRoot); + } else { + Log.w(TAG, "Already fetching data..."); + } + } /** * An interface to communicate when the user change the mount state @@ -87,11 +178,13 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis */ DiskUsageGraph mDiskUsageGraph; private TextView mInfoMsgView; + private GridLayout mLegendLayout; private OnMountListener mOnMountListener; private boolean mIsMountAllowed; private final boolean mIsAdvancedMode; + private boolean mIsInUsageTab = false; /** * Constructor of <code>FilesystemInfoDialog</code>. @@ -108,6 +201,8 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis //Save data this.mMountPoint = mountPoint; + mFetchStatsByTypeTask = new FetchStatsByTypeTask(this.mContext, this); + fetchStats(mMountPoint.getMountPoint()); this.mDiskUsage = diskUsage; this.mIsMountAllowed = false; this.mIsAdvancedMode = @@ -168,6 +263,8 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis this.mDiskUsageGraph = (DiskUsageGraph)contentView.findViewById(R.id.filesystem_disk_usage_graph); + this.mLegendLayout = (GridLayout) contentView.findViewById(R.id.ll_legend); + // Set the user preference about free disk space warning level String fds = Preferences.getSharedPreferences().getString( FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(), @@ -255,6 +352,7 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis public void onClick(View v) { switch (v.getId()) { case R.id.filesystem_info_dialog_tab_info: + mIsInUsageTab = false; if (!this.mInfoViewTab.isSelected()) { this.mInfoViewTab.setSelected(true); ((TextView)this.mInfoViewTab).setTextAppearance( @@ -270,9 +368,11 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis } this.mInfoMsgView.setVisibility( this.mIsMountAllowed || !this.mIsAdvancedMode ? View.GONE : View.VISIBLE); + mLegendLayout.setVisibility(View.INVISIBLE); break; case R.id.filesystem_info_dialog_tab_disk_usage: + mIsInUsageTab = true; if (!this.mDiskUsageViewTab.isSelected()) { this.mInfoViewTab.setSelected(false); ((TextView)this.mInfoViewTab).setTextAppearance( @@ -290,13 +390,19 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis @Override public void run() { //Animate disk usage graph - FilesystemInfoDialog.this.mDiskUsageGraph.drawDiskUsage( - FilesystemInfoDialog.this.mDiskUsage); + FilesystemInfoDialog.this.mDiskUsageGraph.drawDiskUsage(mDiskUsage); + if (mIsInUsageTab) { + if (mLegendLayout.getVisibility() != View.VISIBLE) { + populateLegend(); + mLegendLayout.setVisibility(View.VISIBLE); + } + } } }); break; case R.id.filesystem_info_msg: + mIsInUsageTab = false; //Change the console boolean superuser = ConsoleBuilder.changeToPrivilegedConsole(this.mContext); if (superuser) { @@ -319,9 +425,12 @@ public class FilesystemInfoDialog implements OnClickListener, OnCheckedChangeLis this.mInfoMsgView.setVisibility(View.VISIBLE); this.mIsMountAllowed = false; } + mLegendLayout.setVisibility(View.INVISIBLE); break; default: + mIsInUsageTab = false; + mLegendLayout.setVisibility(View.INVISIBLE); break; } } diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/DiskUsageGraph.java b/src/com/cyanogenmod/filemanager/ui/widgets/DiskUsageGraph.java index ecd0cfa0..bdae373c 100644 --- a/src/com/cyanogenmod/filemanager/ui/widgets/DiskUsageGraph.java +++ b/src/com/cyanogenmod/filemanager/ui/widgets/DiskUsageGraph.java @@ -17,20 +17,22 @@ package com.cyanogenmod.filemanager.ui.widgets; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; +import android.graphics.*; import android.util.AttributeSet; import android.view.View; - +import android.widget.Toast; +import com.cyanogenmod.filemanager.R; import com.cyanogenmod.filemanager.model.DiskUsage; +import com.cyanogenmod.filemanager.model.DiskUsageCategory; import com.cyanogenmod.filemanager.ui.ThemeManager; import com.cyanogenmod.filemanager.ui.ThemeManager.Theme; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; /** * A class that display graphically the usage of a mount point. @@ -38,11 +40,62 @@ import java.util.List; public class DiskUsageGraph extends View { /** + * This is a list for accessing the loaded colors + */ + public static final List<Integer> COLOR_LIST = new ArrayList<Integer>(); + /** + * This is an internal color id reference + */ + private static final List<Integer> INTERNAL_COLOR_LIST = new ArrayList<Integer>() { + { + + // Material Blue + add(R.color.material_palette_blue_1); + add(R.color.material_palette_blue_2); + add(R.color.material_palette_blue_3); + add(R.color.material_palette_blue_4); + + // Material Lime + add(R.color.material_palette_green_1); + add(R.color.material_palette_green_2); + add(R.color.material_palette_green_3); + add(R.color.material_palette_green_4); + + // Material Orange + add(R.color.material_palette_orange_1); + add(R.color.material_palette_orange_2); + add(R.color.material_palette_orange_3); + add(R.color.material_palette_orange_4); + + // Material Pink + add(R.color.material_palette_pink_1); + add(R.color.material_palette_pink_2); + add(R.color.material_palette_pink_3); + add(R.color.material_palette_pink_4); + + + } + }; + + /** + * Initialize the color assets into memory for direct access + */ + private void initializeColors() { + // Only load the colors if needed + if (COLOR_LIST.size() == 0) { + for (int colorId : INTERNAL_COLOR_LIST) { + COLOR_LIST.add(getContext().getResources().getColor(colorId)); + } + } + } + + /** * @hide */ int mDiskWarningAngle = (360 * 95) / 100; - private AnimationDrawingThread mThread; + private static String sWarningText; + /** * @hide */ @@ -50,12 +103,18 @@ public class DiskUsageGraph extends View { Collections.synchronizedList(new ArrayList<DiskUsageGraph.DrawingObject>(2)); /** + * @hide + * drawing objects lock + */ + static final int[] LOCK = new int[0]; + + /** * Constructor of <code>DiskUsageGraph</code>. * * @param context The current context */ public DiskUsageGraph(Context context) { - super(context); + this(context, null); } /** @@ -65,7 +124,7 @@ public class DiskUsageGraph extends View { * @param attrs The attributes of the XML tag that is inflating the view. */ public DiskUsageGraph(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, 0); } /** @@ -80,6 +139,10 @@ public class DiskUsageGraph extends View { */ public DiskUsageGraph(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + initializeColors(); + if (sWarningText == null) { + sWarningText = context.getResources().getString(R.string.pref_disk_usage_warning_level); + } } /** @@ -94,8 +157,8 @@ public class DiskUsageGraph extends View { } /** - * Method that sets the free disk space percentage after the widget change his color - * to advise the user + * Method that sets the free disk space percentage after the widget change his color to advise + * the user * * @param percentage The free disk space percentage */ @@ -103,22 +166,32 @@ public class DiskUsageGraph extends View { this.mDiskWarningAngle = (360 * percentage) / 100; } + // Handle thread for drawing calculations + private Future mAnimationFuture = null; + private static ExecutorService sThreadPool = Executors.newFixedThreadPool(1); + /** * Method that draw the disk usage. * - * @param diskUsage The disk usage + * @param diskUsage {@link com.cyanogenmod.filemanager.model.DiskUsage} The disk usage params */ public void drawDiskUsage(DiskUsage diskUsage) { + // Clear if a current drawing exit - if (this.mThread != null) { - this.mThread.exit(); + if (mAnimationFuture != null && !mAnimationFuture.isCancelled()) { + mAnimationFuture.cancel(true); + } + + // Clear canvas + synchronized (LOCK) { + this.mDrawingObjects.clear(); } - this.mDrawingObjects.clear(); invalidate(); // Start drawing thread - this.mThread = new AnimationDrawingThread(diskUsage); - this.mThread.start(); + AnimationDrawingRunnable animationDrawingRunnable = new AnimationDrawingRunnable(diskUsage); + mAnimationFuture = sThreadPool.submit(animationDrawingRunnable); + } /** @@ -130,32 +203,116 @@ public class DiskUsageGraph extends View { super.onDraw(canvas); //Draw all the drawing objects - int cc = this.mDrawingObjects.size(); - for (int i = 0; i < cc; i++) { - DrawingObject dwo = this.mDrawingObjects.get(i); - canvas.drawArc(dwo.mRectF, dwo.mStartAngle, dwo.mSweepAngle, false, dwo.mPaint); + synchronized (LOCK) { + for (DrawingObject dwo : this.mDrawingObjects) { + canvas.drawArc(dwo.mRectF, dwo.mStartAngle, dwo.mSweepAngle, false, dwo.mPaint); + } } } /** * A thread for drawing the animation of the graph. */ - private class AnimationDrawingThread extends Thread { + private class AnimationDrawingRunnable implements Runnable { private final DiskUsage mDiskUsage; - private boolean mRunning; - private final Object mSync = new Object(); - private int mIndex = 0; + + // Delay in between UI updates and slow down calculations + private static final long ANIMATION_DELAY = 1l; + + // Slop space adjustment for space between segments + private static final int SLOP = 2; + + // flags + private static final boolean USE_COLORS = true; /** * Constructor of <code>AnimationDrawingThread</code>. * * @param diskUsage The disk usage */ - public AnimationDrawingThread(DiskUsage diskUsage) { - super(); + public AnimationDrawingRunnable(DiskUsage diskUsage) { this.mDiskUsage = diskUsage; - this.mRunning = false; + } + + private void sleepyTime() { + try { + Thread.sleep(ANIMATION_DELAY); + } catch (InterruptedException ignored) { + } + } + + private void redrawCanvas() { + //Redraw the canvas + post(new Runnable() { + @Override + public void run() { + invalidate(); + } + }); + } + + private void drawTotal(Rect rect, int stroke) { + // Draw total + DrawingObject drawingObject = createDrawingObject(rect, "disk_usage_total_color", + stroke); + synchronized (LOCK) { + mDrawingObjects.add(drawingObject); + } + while (drawingObject.mSweepAngle < 360) { + drawingObject.mSweepAngle++; + redrawCanvas(); + sleepyTime(); + } + } + + private void drawUsed(Rect rect, int stroke, float used) { + // Draw used + DrawingObject drawingObject = createDrawingObject(rect, "disk_usage_used_color", stroke); + synchronized (LOCK) { + mDrawingObjects.add(drawingObject); + } + while (drawingObject.mSweepAngle < used) { + drawingObject.mSweepAngle++; + redrawCanvas(); + sleepyTime(); + } + } + + private void drawUsedWithColors(Rect rect, int stroke) { + // Draw used segments + if (mDiskUsage != null) { + int lastSweepAngle = 0; + float catUsed = 100.0f; + int color; + int index = 0; + for (DiskUsageCategory category : mDiskUsage.getUsageCategoryList()) { + catUsed = (category.getSizeBytes() * 100) / mDiskUsage.getTotal(); // calc percent + catUsed = (catUsed < 1) ? 1 : catUsed; // Normalize + catUsed = (360 * catUsed) / 100; // calc angle + + // Figure out a color + if (index > -1 && index < COLOR_LIST.size()) { + color = COLOR_LIST.get(index); + index++; + } else { + index = 0; + color = COLOR_LIST.get(index); + } + + DrawingObject drawingObject = createDrawingObjectNoTheme(rect, color, stroke); + drawingObject.mStartAngle += lastSweepAngle; + synchronized (LOCK) { + mDrawingObjects.add(drawingObject); + } + while (drawingObject.mSweepAngle < catUsed + SLOP) { + drawingObject.mSweepAngle++; + redrawCanvas(); + sleepyTime(); + } + lastSweepAngle += drawingObject.mSweepAngle - SLOP; + } + } } /** @@ -172,108 +329,27 @@ public class DiskUsageGraph extends View { rect.top += stroke / 2; rect.bottom -= stroke / 2; - float used = 0.0f; - if (this.mDiskUsage == null) { - used = 100.0f; - } else if (this.mDiskUsage.getTotal() != 0) { + float used = 100.0f; + if (this.mDiskUsage != null && this.mDiskUsage.getTotal() != 0) { used = (this.mDiskUsage.getUsed() * 100) / this.mDiskUsage.getTotal(); } //Translate to angle used = (360 * used) / 100; - synchronized (this.mSync) { - this.mRunning = true; - } - try { - boolean disk_warning = false; - while (this.mRunning) { - //Get the current arc - DrawingObject dwo = null; - if (DiskUsageGraph.this.mDrawingObjects != null - && DiskUsageGraph.this.mDrawingObjects.size() > this.mIndex) { - dwo = DiskUsageGraph.this.mDrawingObjects.get(this.mIndex); - } - - //Draw the total arc circle and then the used arc circle - if (this.mIndex == 0 && dwo == null) { - //Initialize the total arc circle - DiskUsageGraph.this.mDrawingObjects.add( - createDrawingObject( - rect, "disk_usage_total_color", stroke)); //$NON-NLS-1$ - continue; - } - if (this.mIndex == 1 && dwo == null) { - //Initialize the used arc circle - DiskUsageGraph.this.mDrawingObjects.add( - createDrawingObject( - rect, "disk_usage_used_color", stroke)); //$NON-NLS-1$ - continue; - } + // Draws out the graph background color + drawTotal(rect, stroke); - if (this.mIndex == 1 && !disk_warning && - dwo.mSweepAngle >= DiskUsageGraph.this.mDiskWarningAngle) { - Theme theme = ThemeManager.getCurrentTheme(getContext()); - dwo.mPaint.setColor( - theme.getColor( - getContext(), - "disk_usage_used_warning_color")); //$NON-NLS-1$ - disk_warning = true; - } - - //Redraw the canvas - post(new Runnable() { - @Override - public void run() { - invalidate(); - } - }); - - //Next draw call - dwo.mSweepAngle++; - if (this.mIndex >= 1) { - //Only fill until used - if ((dwo.mSweepAngle >= used) || (this.mIndex > 1)) { - synchronized (this.mSync) { - break; //End of the animation - } - } - } - if (dwo.mSweepAngle == 360) { - this.mIndex++; - } - - try { - Thread.sleep(1L); - } catch (Throwable ex) { - /**NON BLOCK**/ - } - } - } finally { - try { - synchronized (this.mSync) { - this.mRunning = false; - this.mSync.notify(); - } - } catch (Throwable ex) { - /**NON BLOCK**/ - } + // Draw the usage + if (USE_COLORS) { + drawUsedWithColors(rect, stroke); + } else { + drawUsed(rect, stroke, used); } - } - /** - * Method that force the thread to exit. - */ - public void exit() { - try { - synchronized (this.mSync) { - if (this.mRunning) { - this.mRunning = false; - this.mSync.wait(); - } - } - } catch (Throwable ex) { - /**NON BLOCK**/ + if (used >= mDiskWarningAngle) { + Toast.makeText(getContext(), sWarningText, Toast.LENGTH_SHORT).show(); } + } /** @@ -282,6 +358,7 @@ public class DiskUsageGraph extends View { * @param rect The area of drawing * @param colorResourceThemeId The theme resource identifier of the color * @param stroke The stroke width + * * @return DrawingObject The drawing object */ private DrawingObject createDrawingObject( @@ -297,6 +374,31 @@ public class DiskUsageGraph extends View { out.mRectF = new RectF(rect); return out; } + + /** + * Method that creates the drawing object. + * + * @param rect The area of drawing + * @param color Integer id of the color + * @param stroke The stroke width + * + * @return DrawingObject The drawing object + * + * [TODO][MSB]: Implement colors for sections into theme + */ + @Deprecated + private DrawingObject createDrawingObjectNoTheme( + Rect rect, int color, int stroke) { + DrawingObject out = new DrawingObject(); + out.mSweepAngle = 0; + out.mPaint.setColor(color); + out.mPaint.setStrokeWidth(stroke); + out.mPaint.setAntiAlias(true); + out.mPaint.setStrokeCap(Paint.Cap.BUTT); + out.mPaint.setStyle(Paint.Style.STROKE); + out.mRectF = new RectF(rect); + return out; + } } /** @@ -304,6 +406,7 @@ public class DiskUsageGraph extends View { */ private class DrawingObject { DrawingObject() {/**NON BLOCK**/} + int mStartAngle = -180; int mSweepAngle = 0; Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); |