android settings源代碼分析(2)

       通過前一篇文章  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);
        }
    }


 

 




 



 

 


 



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章