Merge "Add a music apps view to the Storage Settings."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1931e67..34f3c51 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8165,6 +8165,11 @@
     <!-- Title of games app storage screen [CHAR LIMIT=30] -->
     <string name="game_storage_settings">Games</string>
 
+    <!-- Title for audio files preference. [CHAR LIMIT=50] -->
+    <string name="audio_files_title">Audio files</string>
+    <!-- Title for the Audio storage view. [CHAR LIMIT=50] -->
+    <string name="audio_storage_title">Music</string>
+
     <!-- UI webview setting: WebView uninstalled-for-user explanatory text [CHAR LIMIT=30] -->
     <string name="webview_uninstalled_for_user">Uninstalled for user <xliff:g id="user" example="John Doe">%s</xliff:g>\n</string>
     <!-- UI webview setting: WebView disabled-for-user explanatory text [CHAR LIMIT=30] -->
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 248358c..f5bd2ce 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -25,6 +25,7 @@
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1224,4 +1225,24 @@
             return null;
         }
     }
+
+    /**
+     * Launches an intent which may optionally have a user id defined.
+     * @param fragment Fragment to use to launch the activity.
+     * @param intent Intent to launch.
+     */
+    public static void launchIntent(Fragment fragment, Intent intent) {
+        try {
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
+
+            if (userId == -1) {
+                fragment.startActivity(intent);
+            } else {
+                fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "No activity found for " + intent);
+        }
+    }
+
 }
diff --git a/src/com/android/settings/applications/FileViewHolderController.java b/src/com/android/settings/applications/FileViewHolderController.java
new file mode 100644
index 0000000..e8af722
--- /dev/null
+++ b/src/com/android/settings/applications/FileViewHolderController.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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.android.settings.applications;
+
+import android.app.Fragment;
+import android.view.View;
+
+/**
+ * FileViewHolderController handles adapting the AppViewHolder to work as a general purpose
+ * storage categorization preference in the ManageApplications view.
+ */
+public interface FileViewHolderController {
+    /**
+     * Begins a synchronous query for statistics for the files.
+     */
+    void queryStats();
+
+    /**
+     * Returns if the preference should be shown.
+     */
+    boolean shouldShow();
+
+    /**
+     * Initializes the view within an AppViewHolder.
+     * @param holder The holder to use to initialize.
+     */
+    void setupView(AppViewHolder holder);
+
+    /**
+     * Handles the behavior when the view is clicked.
+     * @param fragment Fragment where the click originated.
+     */
+    void onClick(Fragment fragment);
+}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index f6b303c..443ce34 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications;
 
 import android.app.Activity;
+import android.app.usage.StorageStatsManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -32,6 +33,7 @@
 import android.os.UserManager;
 import android.preference.PreferenceFrameLayout;
 import android.text.TextUtils;
+import android.text.format.Formatter;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -71,6 +73,7 @@
 import com.android.settings.applications.AppStateUsageBridge.UsageState;
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.deviceinfo.storage.StorageStatsSource;
 import com.android.settings.fuelgauge.HighPowerDetail;
 import com.android.settings.fuelgauge.PowerWhitelistBackend;
 import com.android.settings.notification.AppNotificationSettings;
@@ -108,6 +111,7 @@
     // Used for storage only.
     public static final String EXTRA_VOLUME_UUID = "volumeUuid";
     public static final String EXTRA_VOLUME_NAME = "volumeName";
+    public static final String EXTRA_STORAGE_TYPE = "storageType";
 
     private static final String EXTRA_SORT_ORDER = "sortOrder";
     private static final String EXTRA_SHOW_SYSTEM = "showSystem";
@@ -140,6 +144,10 @@
     public static final int FILTER_APPS_WRITE_SETTINGS = 10;
     public static final int FILTER_APPS_INSTALL_SOURCES = 12;
 
+    // Storage types. Used to determine what the extra item in the list of preferences is.
+    public static final int STORAGE_TYPE_DEFAULT = 0;
+    public static final int STORAGE_TYPE_MUSIC = 1;
+
     // This is the string labels for the filter modes above, the order must be kept in sync.
     public static final int[] FILTER_LABELS = new int[]{
             R.string.high_power_filter_on, // High power whitelist, on
@@ -227,6 +235,7 @@
     private ResetAppsHelper mResetAppsHelper;
     private String mVolumeUuid;
     private String mVolumeName;
+    private int mStorageType;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -250,6 +259,7 @@
             if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
                 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
                 mVolumeName = args.getString(EXTRA_VOLUME_NAME);
+                mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
                 mListType = LIST_TYPE_STORAGE;
             } else {
                 // No volume selected, display a normal list, sorted by size.
@@ -316,6 +326,13 @@
                 mApplications.mHasReceivedBridgeCallback =
                         savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
             }
+            if (mStorageType == STORAGE_TYPE_MUSIC) {
+                Context context = getContext();
+                mApplications.setExtraViewController(new MusicViewHolderController(
+                        context,
+                        new StorageStatsSource(context),
+                        mVolumeUuid));
+            }
             mListView.setAdapter(mApplications);
             mListView.setRecyclerListener(mApplications);
             mListView.setFastScrollEnabled(isFastScrollEnabled());
@@ -361,7 +378,11 @@
             mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
         }
         if (mListType == LIST_TYPE_STORAGE) {
-            mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
+            AppFilter filter = new VolumeFilter(mVolumeUuid);
+            if (mStorageType == STORAGE_TYPE_MUSIC) {
+                filter = new CompoundFilter(ApplicationsState.FILTER_AUDIO, filter);
+            }
+            mApplications.setOverrideFilter(filter);
         }
         if (mListType == LIST_TYPE_GAMES) {
             mApplications.setOverrideFilter(ApplicationsState.FILTER_GAMES);
@@ -626,11 +647,17 @@
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        if (mApplications != null && mApplications.getCount() > position) {
+        if (mApplications == null) {
+            return;
+        }
+
+        if (mApplications.getApplicationCount() > position) {
             ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
             mCurrentPkgName = entry.info.packageName;
             mCurrentUid = entry.info.uid;
             startApplicationDetailsActivity();
+        } else {
+            mApplications.mExtraViewController.onClick(this);
         }
     }
 
@@ -769,6 +796,7 @@
         private AppFilter mOverrideFilter;
         private boolean mHasReceivedLoadEntries;
         private boolean mHasReceivedBridgeCallback;
+        private FileViewHolderController mExtraViewController;
 
         // These two variables are used to remember and restore the last scroll position when this
         // fragment is paused. We need this special handling because app entries are added gradually
@@ -839,6 +867,10 @@
             rebuild(true);
         }
 
+        public void setExtraViewController(FileViewHolderController extraViewController) {
+            mExtraViewController = extraViewController;
+        }
+
         public void resume(int sort) {
             if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
             if (!mResumed) {
@@ -889,7 +921,6 @@
                 // Don't rebuild the list until all the app entries are loaded.
                 return;
             }
-            if (DEBUG) Log.i(TAG, "Rebuilding app list...");
             ApplicationsState.AppFilter filterObj;
             Comparator<AppEntry> comparatorObj;
             boolean emulated = Environment.isExternalStorageEmulated();
@@ -924,8 +955,12 @@
                     comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
                     break;
             }
-            filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
 
+            if (mExtraViewController != null) {
+                mExtraViewController.queryStats();
+            }
+
+            filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
             AppFilter finalFilterObj = filterObj;
             mBgHandler.post(() -> {
                 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
@@ -1104,6 +1139,10 @@
         public void onPackageSizeChanged(String packageName) {
             for (int i = 0; i < mActive.size(); i++) {
                 AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
+                ApplicationInfo info = holder.entry.info;
+                if (info == null) {
+                    continue;
+                }
                 if (holder.entry.info.packageName.equals(packageName)) {
                     synchronized (holder.entry) {
                         updateSummary(holder);
@@ -1136,10 +1175,22 @@
         }
 
         public int getCount() {
+            if (mEntries == null) {
+                return 0;
+            }
+            int extraViewAddition =
+                    (mExtraViewController != null && mExtraViewController.shouldShow()) ? 1 : 0;
+            return mEntries.size() + extraViewAddition;
+        }
+
+        public int getApplicationCount() {
             return mEntries != null ? mEntries.size() : 0;
         }
 
         public Object getItem(int position) {
+            if (position == mEntries.size()) {
+                return mExtraViewController;
+            }
             return mEntries.get(position);
         }
 
@@ -1148,6 +1199,9 @@
         }
 
         public long getItemId(int position) {
+            if (position == mEntries.size()) {
+                return -1;
+            }
             return mEntries.get(position).id;
         }
 
@@ -1158,6 +1212,11 @@
 
         @Override
         public boolean isEnabled(int position) {
+            if (position == mEntries.size() && mExtraViewController != null &&
+                    mExtraViewController.shouldShow()) {
+                return true;
+            }
+
             if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
                 return true;
             }
@@ -1172,31 +1231,38 @@
                     convertView);
             convertView = holder.rootView;
 
-            // Bind the data efficiently with the holder
-            ApplicationsState.AppEntry entry = mEntries.get(position);
-            synchronized (entry) {
-                holder.entry = entry;
-                if (entry.label != null) {
-                    holder.appName.setText(entry.label);
+            // Handle the extra view if it is the last entry.
+            if (mEntries != null && mExtraViewController != null && position == mEntries.size()) {
+                mExtraViewController.setupView(holder);
+                convertView.setEnabled(true);
+            } else {
+                // Bind the data efficiently with the holder
+                ApplicationsState.AppEntry entry = mEntries.get(position);
+                synchronized (entry) {
+                    holder.entry = entry;
+                    if (entry.label != null) {
+                        holder.appName.setText(entry.label);
+                    }
+                    mState.ensureIcon(entry);
+                    if (entry.icon != null) {
+                        holder.appIcon.setImageDrawable(entry.icon);
+                    }
+                    updateSummary(holder);
+                    if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        holder.disabled.setVisibility(View.VISIBLE);
+                        holder.disabled.setText(R.string.not_installed);
+                    } else if (!entry.info.enabled) {
+                        holder.disabled.setVisibility(View.VISIBLE);
+                        holder.disabled.setText(R.string.disabled);
+                    } else {
+                        holder.disabled.setVisibility(View.GONE);
+                    }
                 }
-                mState.ensureIcon(entry);
-                if (entry.icon != null) {
-                    holder.appIcon.setImageDrawable(entry.icon);
-                }
-                updateSummary(holder);
-                if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
-                    holder.disabled.setVisibility(View.VISIBLE);
-                    holder.disabled.setText(R.string.not_installed);
-                } else if (!entry.info.enabled) {
-                    holder.disabled.setVisibility(View.VISIBLE);
-                    holder.disabled.setText(R.string.disabled);
-                } else {
-                    holder.disabled.setVisibility(View.GONE);
-                }
+                convertView.setEnabled(isEnabled(position));
             }
+
             mActive.remove(convertView);
             mActive.add(convertView);
-            convertView.setEnabled(isEnabled(position));
             return convertView;
         }
 
diff --git a/src/com/android/settings/applications/MusicViewHolderController.java b/src/com/android/settings/applications/MusicViewHolderController.java
new file mode 100644
index 0000000..18c87db
--- /dev/null
+++ b/src/com/android/settings/applications/MusicViewHolderController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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.android.settings.applications;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.DocumentsContract;
+import android.support.annotation.WorkerThread;
+import android.text.format.Formatter;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.deviceinfo.storage.StorageStatsSource;
+
+/**
+ * MusicViewHolderController controls an Audio/Music file view in the ManageApplications view.
+ */
+public class MusicViewHolderController implements FileViewHolderController {
+    private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
+
+    private Context mContext;
+    private StorageStatsSource mSource;
+    private String mVolumeUuid;
+    private long mMusicSize;
+
+    public MusicViewHolderController(
+            Context context, StorageStatsSource source, String volumeUuid) {
+        mContext = context;
+        mSource = source;
+        mVolumeUuid = volumeUuid;
+    }
+
+    @Override
+    @WorkerThread
+    public void queryStats() {
+        mMusicSize = mSource.getExternalStorageStats(mVolumeUuid, UserHandle.CURRENT).audioBytes;
+    }
+
+    @Override
+    public boolean shouldShow() {
+        return true;
+    }
+
+    @Override
+    public void setupView(AppViewHolder holder) {
+        holder.appIcon.setImageDrawable(mContext.getDrawable(R.drawable.empty_icon));
+        holder.appName.setText(mContext.getText(R.string.audio_files_title));
+        holder.summary.setText(Formatter.formatFileSize(mContext, mMusicSize));
+    }
+
+    @Override
+    public void onClick(Fragment fragment) {
+        Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+        intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root"));
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.putExtra(Intent.EXTRA_USER_ID, UserHandle.CURRENT);
+        Utils.launchIntent(fragment, intent);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
index b0a9902..ce26c0f 100644
--- a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
@@ -533,7 +533,7 @@
         if (intent != null) {
             intent.putExtra(Intent.EXTRA_USER_ID, userId);
 
-            launchIntent(this, intent);
+            Utils.launchIntent(this, intent);
             return true;
         }
         return super.onPreferenceTreeClick(pref);
@@ -670,20 +670,6 @@
         return total;
     }
 
-    private static void launchIntent(Fragment fragment, Intent intent) {
-        try {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
-
-            if (userId == -1) {
-                fragment.startActivity(intent);
-            } else {
-                fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
-            }
-        } catch (ActivityNotFoundException e) {
-            Log.w(TAG, "No activity found for " + intent);
-        }
-    }
-
     private final StorageEventListener mStorageListener = new StorageEventListener() {
         @Override
         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -815,7 +801,7 @@
                     new DialogInterface.OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
-                            launchIntent(OtherInfoFragment.this, intent);
+                            Utils.launchIntent(OtherInfoFragment.this, intent);
                         }
                     });
             builder.setNegativeButton(android.R.string.cancel, null);
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 22f9c4c..16dcd18 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -243,10 +243,15 @@
     }
 
     private Intent getAudioIntent() {
-        Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
-        intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root"));
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        return intent;
+        Bundle args = new Bundle();
+        args.putString(ManageApplications.EXTRA_CLASSNAME,
+                Settings.StorageUseActivity.class.getName());
+        args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
+        args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
+        args.putInt(ManageApplications.EXTRA_STORAGE_TYPE, ManageApplications.STORAGE_TYPE_MUSIC);
+        return Utils.onBuildStartFragmentIntent(mContext,
+                ManageApplications.class.getName(), args, null, R.string.audio_storage_title, null,
+                false);
     }
 
     private Intent getAppsIntent() {
diff --git a/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java b/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java
new file mode 100644
index 0000000..b6e03fb
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source 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.android.settings.deviceinfo.storage;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.os.UserHandle;
+
+/**
+ * StorageStatsSource wraps the StorageStatsManager for testability purposes.
+ */
+public class StorageStatsSource {
+    private StorageStatsManager mSsm;
+
+    public StorageStatsSource(Context context) {
+        mSsm = context.getSystemService(StorageStatsManager.class);
+    }
+
+    public ExternalStorageStats getExternalStorageStats(String volumeUuid, UserHandle user) {
+        return new ExternalStorageStats(mSsm.queryExternalStatsForUser(volumeUuid, user));
+    }
+
+    public static class ExternalStorageStats {
+        public long totalBytes;
+        public long audioBytes;
+        public long videoBytes;
+        public long imageBytes;
+
+        public ExternalStorageStats(long totalBytes, long audioBytes, long videoBytes,
+                long imageBytes) {
+            this.totalBytes = totalBytes;
+            this.audioBytes = audioBytes;
+            this.videoBytes = videoBytes;
+            this.imageBytes = imageBytes;
+        }
+
+        public ExternalStorageStats(android.app.usage.ExternalStorageStats stats) {
+            totalBytes = stats.getTotalBytes();
+            audioBytes = stats.getAudioBytes();
+            videoBytes = stats.getVideoBytes();
+            imageBytes = stats.getImageBytes();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/MusicViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/MusicViewHolderControllerTest.java
new file mode 100644
index 0000000..2a2de66
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/MusicViewHolderControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source 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.android.settings.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
+import android.provider.DocumentsContract;
+import android.view.LayoutInflater;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.deviceinfo.storage.StorageStatsSource;
+import com.android.settingslib.deviceinfo.StorageVolumeProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class MusicViewHolderControllerTest {
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Fragment mFragment;
+    @Mock
+    private StorageVolumeProvider mSvp;
+    @Mock
+    private StorageStatsSource mSource;
+
+    private Context mContext;
+    private MusicViewHolderController mController;
+    private VolumeInfo mVolume;
+    private AppViewHolder mHolder;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mVolume = new VolumeInfo("id", 0, null, "id");
+        mController = new MusicViewHolderController(mContext, mSource, mVolume.fsUuid);
+
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        mHolder = AppViewHolder.createOrRecycle(inflater, null);
+    }
+
+    @Test
+    public void storageShouldBeZeroBytesIfQueriedBeforeStorageQueryFinishes() {
+        mController.setupView(mHolder);
+
+        assertThat(mHolder.summary.getText().toString()).isEqualTo("0.00B");
+    }
+
+    @Test
+    public void storageShouldRepresentStorageStatsQuery() {
+        when(mSource.getExternalStorageStats(any(String.class), any(UserHandle.class))).thenReturn(
+                new StorageStatsSource.ExternalStorageStats(1, 1, 0, 0));
+
+        mController.queryStats();
+        mController.setupView(mHolder);
+
+        assertThat(mHolder.summary.getText().toString()).isEqualTo("1.00B");
+    }
+
+    @Test
+    public void clickingShouldIntentIntoFilesApp() {
+        mController.onClick(mFragment);
+
+        final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mFragment).startActivity(argumentCaptor.capture());
+        Intent intent = argumentCaptor.getValue();
+
+        assertThat(intent.getAction()).isEqualTo(DocumentsContract.ACTION_BROWSE);
+        assertThat(intent.getData()).isEqualTo(DocumentsContract.buildRootUri(
+                "com.android.providers.media.documents",
+                "audio_root"));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index a377505..f7baba3 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -35,6 +35,7 @@
 import android.widget.LinearLayout;
 
 import com.android.settings.R;
+import com.android.settings.Settings;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.SubSettings;
@@ -118,12 +119,15 @@
         final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(),
                 any(UserHandle.class));
-
         Intent intent = argumentCaptor.getValue();
-        assertThat(intent.getAction()).isEqualTo(DocumentsContract.ACTION_BROWSE);
-        assertThat(intent.getData()).isEqualTo(DocumentsContract.buildRootUri(
-                "com.android.providers.media.documents",
-                "audio_root"));
+
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
+        assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
+        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
+                ManageApplications.class.getName());
+        assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS).getInt(
+                ManageApplications.EXTRA_STORAGE_TYPE, 0)).isEqualTo(
+                ManageApplications.STORAGE_TYPE_MUSIC);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
index fa0851e..990223a 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
@@ -61,7 +61,6 @@
         final View view =
                 inflater.inflate(
                         mPreference.getLayoutResource(), new LinearLayout(mContext), false);
-
         mHolder = new PreferenceViewHolder(view);
     }