Storage
从一级菜单的布局文件top_level_settings.xml文件可以看到:
<Preference
android:key="top_level_storage"
android:title="@string/storage_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_storage"
android:order="-60"
android:fragment="com.android.settings.deviceinfo.StorageSettings"
settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
storage指定的fragment是StorageSettings,指定的controller是TopLevelStoragePreferenceController。
在TopLevelStoragePreferenceController里面主要就是控制storage的可见否与更新summary,在Controller里面更新summary的方法里,通过线程工具在使用的storage发生改变后,会同步更新一级菜单里storage这个preference的summary:
ThreadUtils.postOnBackgroundThread(() -> {
final NumberFormat percentageFormat = NumberFormat.getPercentInstance();
final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
mStorageManagerVolumeProvider);
final double privateUsedBytes = info.totalBytes - info.freeBytes;
ThreadUtils.postOnMainThread(() -> {
preference.setSummary(mContext.getString(R.string.storage_summary,
percentageFormat.format(privateUsedBytes / info.totalBytes),
Formatter.formatFileSize(mContext, info.freeBytes)));
});
});
info.totalBytes指的是设备总的内存空间,info.freeBytes是设备还剩余的空间,privateUsedBytes是已经使用来的设备内存,percentageFormat.format(privateUsedBytes / info.totalBytes),就是设置一级菜单中storage的preference的summary显示已经使用了百分之多少。
StorageSettings主要就是“ 显示内部存储(包括内置存储和私有存储)的面板卷)和可移动存储(公共卷)。”就是设置各个部分使用的内存。
if (mInternalCategory.getPreferenceCount() == 2
&& mExternalCategory.getPreferenceCount() == 0) {
// Only showing primary internal storage, so just shortcut
if (!mHasLaunchedPrivateVolumeSettings) {
mHasLaunchedPrivateVolumeSettings = true;
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
if(Utils.isMonkeyRunning()){
Log.e(TAG, "Monkey test running so finishing storage manager dashboard settings");
}
else {
new SubSettingLauncher(getActivity())
.setDestination(StorageDashboardFragment.class.getName())
.setArguments(args)
.setTitleRes(R.string.storage_settings)
.setSourceMetricsCategory(getMetricsCategory())
.launch();
}
finish();
}
从setDestination(StorageDashboardFragment.class.getName())可以了解到,它加载了StorageDashboardFragment.class这个类。
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/deviceinfo
/StorageDashboardFragment.java
在StorageDashboardFragment.java中加载的xml文件是storage_dashboard_fragment.xml
@Override
protected int getPreferenceScreenResId() {
return R.xml.storage_dashboard_fragment;
}
vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/storage_dashboard_fragment.xml
<com.android.settings.deviceinfo.StorageItemPreference
android:key="pref_photos_videos"
android:title="@string/storage_photos_videos"
android:icon="@drawable/ic_photo_library"
settings:allowDividerAbove="true"
android:order="2" />
<com.android.settings.deviceinfo.StorageItemPreference
android:key="pref_music_audio"
android:title="@string/storage_music_audio"
android:icon="@drawable/ic_media_stream"
android:order="3" />
从资源文件可以看出,这个就是显示storage各个部分使用内存详细情况。storage里面详细情况的展示分为两个部分,一部分是上面的磁盘展示,另一部分是下面的item展示。
protected int getPreferenceScreenResId() {
return R.xml.storage_dashboard_fragment;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mSummaryController = new StorageSummaryDonutPreferenceController(context);
controllers.add(mSummaryController);
上面部分磁盘展示的controller就是StorageSummaryDonutPreferenceController,
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/deviceinfo/storage
/StorageSummaryDonutPreferenceController.java
public void updateState(Preference preference) {
super.updateState(preference);
StorageSummaryDonutPreference summary = (StorageSummaryDonutPreference) preference;
summary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes));
summary.setSummary(mContext.getString(R.string.storage_volume_total,
Formatter.formatShortFileSize(mContext, mTotalBytes)));
summary.setPercent(mUsedBytesStorageSummaryDonutPreference, mTotalBytes);
Log.d("StorageSummaryDonutPreferenceController","mUsedBytes: "+mUsedBytes+" mTotalBytes: "+mTotalBytes);
summary.setEnabled(true);
}
updateState函数主要是更改storage磁盘上面的summary字符串显示,mUsedBytes是已用的内存,mTotalBytes是全部的内存。而在updateState方法中更改的summary,StorageSummaryDonutPreference就是布局的主要显示。
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/deviceinfo/storage
/StorageSummaryDonutPreference.java
setLayoutResource(R.layout.storage_summary_donut);
加载的布局文件是storage_summary_donut.xml
public void setPercent(long usedBytes, long totalBytes) {
if (totalBytes == 0) {
return;
}
mPercent = usedBytes / (double) totalBytes;
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view); donut.setPercentage(mPercent);
view.itemView.setClickable(false);
final DonutView donut = (DonutView) view.findViewById(R.id.donut);
if (donut != null) {
donut.setPercentage(mPercent);
}
final Button deletionHelperButton = (Button) view.findViewById(R.id.deletion_helper_button);
if (deletionHelperButton != null) {
deletionHelperButton.setOnClickListener(this);
}
}
通过setPercent()来计算内存空间占用的百分比,然后通过onBindViewHolder()方法来加载布局, donut.setPercentage(mPercent)设置显示的百分比。
然后是下面的item部分的显示
StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
mVolume, new StorageManagerVolumeProvider(sm));
controllers.add(mPreferenceController);
item部分的controller可以看到是StorageItemPreferenceController。
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/deviceinfo/storage
/StorageItemPreferenceController.java
private StorageItemPreference mPhotoPreference;
private StorageItemPreference mAudioPreference;
private StorageItemPreference mGamePreference;
private StorageItemPreference mMoviesPreference;
private StorageItemPreference mAppPreference;
private StorageItemPreference mFilePreference;
private StorageItemPreference mSystemPreference;
可以看到,初始化了item就是StorageItemPreference,
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
Log.d("doubleway","mScreen getKey : "+mScreen.getKey());
mPhotoPreference = screen.findPreference(PHOTO_KEY);
mAudioPreference = screen.findPreference(AUDIO_KEY);
mGamePreference = screen.findPreference(GAME_KEY);
mMoviesPreference = screen.findPreference(MOVIES_KEY);
mAppPreference = screen.findPreference(OTHER_APPS_KEY);
mSystemPreference = screen.findPreference(SYSTEM_KEY);
mFilePreference = screen.findPreference(FILES_KEY);
setFilesPreferenceVisibility();
}
然后通过findPreference方法加载不同的Key来显示item,mScreen是/storage_dashboard_fragment.xml。
还监听了item的点击事件;
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference == null) {
return false;
}
Intent intent = null;
if (preference.getKey() == null) {
return false;
}
switch (preference.getKey()) {
case PHOTO_KEY:
Log.d("doubleway","photo onclick");
intent = getPhotosIntent();
break;
case AUDIO_KEY:
intent = getAudioIntent();
if (intent != null) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
launchIntent(intent);
return true;
}
捕获到点击事件以后,获取item 的intent,然后加载itent。
以getPhotosIntent为例,来看他是怎样获得intent的。
private Intent getPhotosIntent() {
Bundle args = getWorkAnnotatedBundle(2);
args.putString(
ManageApplications.EXTRA_CLASSNAME, Settings.PhotosStorageActivity.class.getName());
args.putInt(
ManageApplications.EXTRA_STORAGE_TYPE,
ManageApplications.STORAGE_TYPE_PHOTOS_VIDEOS);
return new SubSettingLauncher(mContext)
.setDestination(ManageApplications.class.getName())
.setTitleRes(R.string.storage_photos_videos)
.setArguments(args)
.setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
.toIntent();
}
可以看到PhotosStorageActivity这个类是一个Settings的内部类,并没有具体实现。它setDestination的是ManageApplications.class。
if (mListType == LIST_TYPE_OVERLAY && !Utils.isSystemAlertWindowEnabled(getContext())) {
mRootView = inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
setHasOptionsMenu(false);
return mRootView;
}
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
mRecyclerView = mListContainer.findViewById(R.id.apps_list);
可以看到ManageApplications里面加载的布局根据判断加的是manage_applications_apps_unsupported或者是manage_applications_apps,然后下面的app列表是一个RecyclerView,apps_list。