通過前一篇文章 android settings源代碼分析(1) 分析,大概知道了Settings主頁面是如何顯示,今天主要分析“應用”這一塊google是如何實現的。
應用對應的fragment爲:
<span style="font-size:14px;"> <!-- Application Settings -->
<header
android:fragment="com.android.settings.applications.ManageApplications"
android:icon="@drawable/ic_settings_applications"
android:title="@string/applications_settings"
android:id="@+id/application_settings" /></span>
因此需要查看ManageApplications如何實現。
ManageApplications所在路徑爲:
kikat_4.4_CTS\packages\apps\Settings\src\com\android\settings\applications
從Application UI可以看出,fragment主要是一個tab,以及每一個tab下都會顯示和存儲相關的信息,比如RAM,SDCARD和內部存儲空間的大小。接下來分析tab如何實現以及這些存儲信息如何獲取。
查看ManageApplications的onCreateView函數,可以看到:
View rootView = mInflater.inflate(R.layout.manage_applications_content,
container, false);
這裏會使用manage_applications_content.xml,我們查看xml的內容:
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, 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.
*/
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<android.support.v4.view.PagerTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textAppearance="@style/TextAppearance.PagerTabs"
android:padding="0dp">
</android.support.v4.view.PagerTabStrip>
</android.support.v4.view.ViewPager>
</LinearLayout>
application fragment的佈局本質就是一個ViewPager,因此可以支持左右滑動,每一頁對應一個tab的顯示。
MyPagerAdapter adapter = new MyPagerAdapter();
mViewPager.setAdapter(adapter);
mViewPager.setOnPageChangeListener(adapter);
這裏會設置一個adapter,用來填充ViewPager裏的內容。
class MyPagerAdapter extends PagerAdapter
implements ViewPager.OnPageChangeListener {
int mCurPos = 0;
@Override
public int getCount() {
return mNumTabs;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TabInfo tab = mTabs.get(position);
View root = tab.build(mInflater, mContentContainer, mRootView);
container.addView(root);
root.setTag(R.id.name, tab);
return root;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public int getItemPosition(Object object) {
return super.getItemPosition(object);
//return ((TabInfo)((View)object).getTag(R.id.name)).mListType;
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).mLabel;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mCurPos = position;
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
updateCurrentTab(mCurPos);
}
}
}
此adapter中instantiateItem函數會初始化每一個子項,即每一頁:
TabInfo tab = mTabs.get(position);
View root = tab.build(mInflater, mContentContainer, mRootView);
因此需要查看TabInfo中的build函數。
public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) {
if (mRootView != null) {
return mRootView;
}
mInflater = inflater;
mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING
? R.layout.manage_applications_running
: R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
mLoadingContainer.setVisibility(View.VISIBLE);
mListContainer = mRootView.findViewById(R.id.list_container);
if (mListContainer != null) {
// Create adapter and list view here
View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
if (emptyView != null) {
lv.setEmptyView(emptyView);
}
lv.setOnItemClickListener(this);
lv.setSaveEnabled(true);
lv.setItemsCanFocus(true);
lv.setTextFilterEnabled(true);
mListView = lv;
mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
mListView.setAdapter(mApplications);
mListView.setRecyclerListener(mApplications);
mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false);
if (mFilter == FILTER_APPS_SDCARD) {
mStorageChartLabel.setText(mOwner.getActivity().getText(
R.string.sd_card_storage));
} else {
mStorageChartLabel.setText(mOwner.getActivity().getText(
R.string.internal_storage));
}
applyCurrentStorage();
}
mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
R.id.running_processes);
if (mRunningProcessesView != null) {
mRunningProcessesView.doCreate(mSavedInstanceState);
}
return mRootView;
}
此函數中用來顯示tab中listview和tab下對應的storage顯示信息。
對於tab中listView的填充:
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, 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);
}
mState.ensureIcon(entry);
if (entry.icon != null) {
holder.appIcon.setImageDrawable(entry.icon);
}
holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
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);
}
if (mFilterMode == FILTER_APPS_SDCARD) {
holder.checkBox.setVisibility(View.VISIBLE);
holder.checkBox.setChecked((entry.info.flags
& ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
} else {
holder.checkBox.setVisibility(View.GONE);
}
}
mActive.remove(convertView);
mActive.add(convertView);
return convertView;
}
對於storage的獲取,需要使用到IMediaContainerService,綁定此service的地方:
getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);
連接此service,每一個tab都會得到此service實例,通過此實例獲取storage相關信息
private volatile IMediaContainerService mContainerService;
private final ServiceConnection mContainerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mContainerService = IMediaContainerService.Stub.asInterface(service);
for (int i=0; i<mTabs.size(); i++) {
mTabs.get(i).setContainerService(mContainerService);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mContainerService = null;
}
};
獲取storage信息代碼如下:
獲取SD卡:
if (mFilter == FILTER_APPS_SDCARD) {
if (mContainerService != null) {
try {
final long[] stats = mContainerService.getFileSystemStats(
Environment.getExternalStorageDirectory().getPath());
mTotalStorage = stats[0];
mFreeStorage = stats[1];
} catch (RemoteException e) {
Log.w(TAG, "Problem in container service", e);
}
}
if (mApplications != null) {
final int N = mApplications.getCount();
for (int i=0; i<N; i++) {
ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
mAppStorage += ae.externalCodeSize + ae.externalDataSize
+ ae.externalCacheSize;
}
}
獲取本地存儲空間信息:
if (mContainerService != null) {
try {
final long[] stats = mContainerService.getFileSystemStats(
Environment.getDataDirectory().getPath());
mTotalStorage = stats[0];
mFreeStorage = stats[1];
} catch (RemoteException e) {
Log.w(TAG, "Problem in container service", e);
}
}
最後顯示到UI上去:
void applyCurrentStorage() {
// If view hierarchy is not yet created, no views to update.
if (mRootView == null) {
return;
}
if (mTotalStorage > 0) {
BidiFormatter bidiFormatter = BidiFormatter.getInstance();
mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage,
mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage);
long usedStorage = mTotalStorage - mFreeStorage;
if (mLastUsedStorage != usedStorage) {
mLastUsedStorage = usedStorage;
String sizeStr = bidiFormatter.unicodeWrap(
Formatter.formatShortFileSize(mOwner.getActivity(), usedStorage));
mUsedStorageText.setText(mOwner.getActivity().getResources().getString(
R.string.service_foreground_processes, sizeStr));
}
if (mLastFreeStorage != mFreeStorage) {
mLastFreeStorage = mFreeStorage;
String sizeStr = bidiFormatter.unicodeWrap(
Formatter.formatShortFileSize(mOwner.getActivity(), mFreeStorage));
mFreeStorageText.setText(mOwner.getActivity().getResources().getString(
R.string.service_background_processes, sizeStr));
}
} else {
mColorBar.setRatios(0, 0, 0);
if (mLastUsedStorage != -1) {
mLastUsedStorage = -1;
mUsedStorageText.setText("");
}
if (mLastFreeStorage != -1) {
mLastFreeStorage = -1;
mFreeStorageText.setText("");
}
}
}
當點擊listView子項時,會跳轉到詳細列表頁面:
public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position,
long id) {
if (tab.mApplications != null && tab.mApplications.getCount() > position) {
ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position);
mCurrentPkgName = entry.info.packageName;
startApplicationDetailsActivity();
}
}
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// start new fragment to display extended information
Bundle args = new Bundle();
args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName);
PreferenceActivity pa = (PreferenceActivity)getActivity();
pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
R.string.application_info_label, null, this, INSTALLED_APP_DETAILS);
}
查看詳細列表對應的類:
public class InstalledAppDetails extends Fragment
implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
ApplicationsState.Callbacks
即也是一個fragment。
詳細列表界面相關button操作代碼如下:
public void onClick(View v) {
String packageName = mAppEntry.info.packageName;
if(v == mUninstallButton) {
if (mUpdatedSysApp) {
showDialogInner(DLG_FACTORY_RESET, 0);
} else {
if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (mAppEntry.info.enabled) {
showDialogInner(DLG_DISABLE, 0);
} else {
new DisableChanger(this, mAppEntry.info,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
.execute((Object)null);
}
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
uninstallPkg(packageName, true, false);
} else {
uninstallPkg(packageName, false, false);
}
}
} else if(v == mSpecialDisableButton) {
showDialogInner(DLG_SPECIAL_DISABLE, 0);
} else if(v == mActivitiesButton) {
mPm.clearPackagePreferredActivities(packageName);
try {
mUsbManager.clearDefaults(packageName, UserHandle.myUserId());
} catch (RemoteException e) {
Log.e(TAG, "mUsbManager.clearDefaults", e);
}
mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
TextView autoLaunchTitleView =
(TextView) mRootView.findViewById(R.id.auto_launch_title);
TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
} else if(v == mClearDataButton) {
if (mAppEntry.info.manageSpaceActivityName != null) {
if (!Utils.isMonkeyRunning()) {
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(mAppEntry.info.packageName,
mAppEntry.info.manageSpaceActivityName);
startActivityForResult(intent, REQUEST_MANAGE_SPACE);
}
} else {
showDialogInner(DLG_CLEAR_DATA, 0);
}
} else if (v == mClearCacheButton) {
// Lazy initialization of observer
if (mClearCacheObserver == null) {
mClearCacheObserver = new ClearCacheObserver();
}
mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
} else if (v == mForceStopButton) {
showDialogInner(DLG_FORCE_STOP, 0);
//forceStopPackage(mAppInfo.packageName);
} else if (v == mMoveAppButton) {
if (mPackageMoveObserver == null) {
mPackageMoveObserver = new PackageMoveObserver();
}
int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
mMoveInProgress = true;
refreshButtons();
mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
}
}