am 728ac353
: New Running Services UI.
This commit is contained in:
@@ -350,7 +350,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="ManageApplications"
|
||||
<activity android:name=".applications.ManageApplications"
|
||||
android:label="@string/manageapplications_settings_title"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
@@ -365,15 +365,22 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="InstalledAppDetails" android:label="@string/application_info_label">
|
||||
<!-- Keep compatibility with old shortcuts. -->
|
||||
<activity-alias android:name=".ManageApplications"
|
||||
android:targetActivity=".applications.ManageApplications"
|
||||
android:exported="true" />
|
||||
|
||||
<activity android:name=".applications.InstalledAppDetails"
|
||||
android:label="@string/application_info_label">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="RunningServices"
|
||||
<!--
|
||||
<activity android:name=".applications.RunningServices"
|
||||
android:label="@string/runningservices_settings_title"
|
||||
android:clearTaskOnLaunch="true">
|
||||
<intent-filter>
|
||||
@@ -384,6 +391,24 @@
|
||||
<category android:name="com.android.settings.SHORTCUT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
-->
|
||||
|
||||
<!-- Provide direct entry into manage apps showing running services. -->
|
||||
<activity-alias android:name=".RunningServices"
|
||||
android:targetActivity=".applications.ManageApplications">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.MONKEY" />
|
||||
<category android:name="android.intent.category.VOICE_LAUNCH" />
|
||||
<category android:name="com.android.settings.SHORTCUT" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<activity android:name=".applications.RunningServiceDetails"
|
||||
android:theme="@android:style/Theme.NoTitleBar"
|
||||
android:label="@string/runningservicedetails_settings_title">
|
||||
</activity>
|
||||
|
||||
<activity android:name="SecuritySettings"
|
||||
android:label="@string/location_security_settings_title"
|
||||
|
@@ -14,9 +14,20 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<ListView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/list"
|
||||
android:drawSelectorOnTop="false"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<ListView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/list"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<view class="com.android.settings.applications.RunningProcessesView"
|
||||
android:id="@+id/running_processes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
@@ -31,7 +31,6 @@
|
||||
<ImageView android:id="@+id/app_icon"
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_marginRight="11dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:scaleType="fitCenter"/>
|
||||
|
94
res/layout/running_processes_item.xml
Normal file
94
res/layout/running_processes_item.xml
Normal file
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2010, 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="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
android:gravity="fill" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:paddingRight="6dip"
|
||||
android:paddingLeft="6dip"
|
||||
android:gravity="center_vertical" >
|
||||
|
||||
<ImageView android:id="@+id/icon"
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:layout_marginRight="11dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:scaleType="fitCenter"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:baselineAlignedChildIndex="0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingRight="4dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_marginBottom="2dip" />
|
||||
<TextView android:id="@+id/size"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:baselineAlignedChildIndex="0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView android:id="@+id/description"
|
||||
android:layout_gravity="center_vertical|left"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingRight="4dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
<TextView android:id="@+id/uptime"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
60
res/layout/running_processes_view.xml
Normal file
60
res/layout/running_processes_view.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* 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">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0px"
|
||||
android:layout_weight="1">
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:fastScrollEnabled="true" />
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_running_services"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
</FrameLayout>
|
||||
<view class="com.android.settings.applications.RunningProcessesView$LinearColorBar"
|
||||
android:id="@+id/color_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="4dp">
|
||||
<TextView android:id="@+id/foregroundText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmallInverse"
|
||||
android:color="?android:attr/textColorPrimaryInverse"
|
||||
android:singleLine="true" />
|
||||
<TextView android:id="@+id/backgroundText"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:textAppearance="?android:attr/textAppearanceSmallInverse"
|
||||
android:color="?android:attr/textColorPrimaryInverse"
|
||||
android:singleLine="true" />
|
||||
</view>
|
||||
</LinearLayout>
|
36
res/layout/running_service_details.xml
Normal file
36
res/layout/running_service_details.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2010, 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.
|
||||
*/
|
||||
-->
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:id="@+id/all_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Summary information as per previous screen -->
|
||||
<include
|
||||
layout="@layout/running_processes_item"
|
||||
android:id="@+id/snippet"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
37
res/layout/running_service_details_process.xml
Normal file
37
res/layout/running_service_details_process.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2010, 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">
|
||||
|
||||
<!-- Icon and label of the service. -->
|
||||
<include
|
||||
layout="@layout/running_processes_item"
|
||||
android:id="@+id/service"/>
|
||||
|
||||
<TextView android:id="@+id/comp_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="14dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</LinearLayout>
|
48
res/layout/running_service_details_service.xml
Normal file
48
res/layout/running_service_details_service.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2010, 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">
|
||||
|
||||
<!--
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="?android:attr/listDivider"/>
|
||||
-->
|
||||
|
||||
<!-- Icon and label of the service. -->
|
||||
<include
|
||||
layout="@layout/running_processes_item"
|
||||
android:id="@+id/service"/>
|
||||
|
||||
<TextView android:id="@+id/comp_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="14dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<include
|
||||
layout="@layout/two_buttons_panel"
|
||||
android:id="@+id/control_buttons_panel"/>
|
||||
|
||||
</LinearLayout>
|
@@ -35,7 +35,7 @@
|
||||
android:text="@string/no_running_services"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
</FrameLayout>
|
||||
<view class="com.android.settings.RunningServices$LinearColorBar"
|
||||
<view class="com.android.settings.applications.RunningServices$LinearColorBar"
|
||||
android:id="@+id/color_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
21
res/layout/separator_label.xml
Normal file
21
res/layout/separator_label.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="?android:attr/listSeparatorTextViewStyle" />
|
@@ -1800,15 +1800,54 @@ found in the list of installed applications.</string>
|
||||
<!-- Running services, button to cancel stopping of a service -->
|
||||
<string name="confirm_stop_cancel">Cancel</string>
|
||||
<!-- Running services, description for a service in the started state -->
|
||||
<string name="service_started_by_app">Started by application: touch to stop</string>
|
||||
<string name="service_started_by_app">Started by application.</string>
|
||||
<!-- Running services, description for a service in the started state -->
|
||||
<string name="service_client_name"><xliff:g id="client_name">%1$s</xliff:g>: touch to manage</string>
|
||||
<string name="service_client_name"><xliff:g id="client_name">%1$s</xliff:g></string>
|
||||
<!-- Running services, summary of background processes -->
|
||||
<string name="service_background_processes">Avail: <xliff:g id="free">%2$s</xliff:g>+<xliff:g id="memory">%3$s</xliff:g> in <xliff:g id="count">%1$d</xliff:g></string>
|
||||
<string name="service_background_processes"><xliff:g id="memory">%1$s</xliff:g> available</string>
|
||||
<!-- Running services, summary of foreground processes -->
|
||||
<string name="service_foreground_processes">Other: <xliff:g id="memory">%2$s</xliff:g> in <xliff:g id="count">%1$d</xliff:g></string>
|
||||
<string name="service_foreground_processes"><xliff:g id="memory">%1$s</xliff:g> in use</string>
|
||||
<!-- Text to label a process entry with the process name. -->
|
||||
<string name="service_process_name">Process: <xliff:g id="process">%1$s</xliff:g></string>
|
||||
<string name="service_process_name"><xliff:g id="process">%1$s</xliff:g></string>
|
||||
<!-- Descriptive text of a running process: singular process, singular service. -->
|
||||
<string name="running_processes_item_description_s_s"><xliff:g id="numprocess">%1$d</xliff:g>
|
||||
process and <xliff:g id="numservices">%2$d</xliff:g> service</string>
|
||||
<!-- Descriptive text of a running process: singular process, plural service. -->
|
||||
<string name="running_processes_item_description_s_p"><xliff:g id="numprocess">%1$d</xliff:g>
|
||||
process and <xliff:g id="numservices">%2$d</xliff:g> services</string>
|
||||
<!-- Descriptive text of a running process: plural process, singular service. -->
|
||||
<string name="running_processes_item_description_p_s"><xliff:g id="numprocess">%1$d</xliff:g>
|
||||
processes and <xliff:g id="numservices">%2$d</xliff:g> service</string>
|
||||
<!-- Descriptive text of a running process: plural process, plural service. -->
|
||||
<string name="running_processes_item_description_p_p"><xliff:g id="numprocess">%1$d</xliff:g>
|
||||
processes and <xliff:g id="numservices">%2$d</xliff:g> services</string>
|
||||
|
||||
<!-- Details about an application's running services. -->
|
||||
<string name="runningservicedetails_settings_title">Running application</string>
|
||||
<!-- Message displayed when there are no active services in a process. -->
|
||||
<string name="no_services">Not active</string>
|
||||
<!-- Title for list of services. -->
|
||||
<string name="runningservicedetails_services_title">Services</string>
|
||||
<!-- Title for list of services. -->
|
||||
<string name="runningservicedetails_processes_title">Processes</string>
|
||||
<!-- Running service details, stop a service that has started itself. -->
|
||||
<string name="service_stop">Stop</string>
|
||||
<!-- Running service details, manage a service that is running for some other reason. -->
|
||||
<string name="service_manage">Manage</string>
|
||||
<!-- Running service details, default description for services that are started. -->
|
||||
<string name="service_stop_description">This service was started by its
|
||||
application. Stopping it may cause the application to misbehave.</string>
|
||||
<!-- Running service details, default description for services that are managed. -->
|
||||
<string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
|
||||
currently in use. You can manage your settings to stop it.</string>
|
||||
<!-- Description of the main process in the details. -->
|
||||
<string name="main_running_process_description">Main process that is in use.</string>
|
||||
<!-- Message that a process's service is in use. -->
|
||||
<string name="process_service_in_use_description">Service <xliff:g id="comp_name">%1$s</xliff:g>
|
||||
is in use.</string>
|
||||
<!-- Message that a process's provider is in use. -->
|
||||
<string name="process_provider_in_use_description">Provider <xliff:g id="comp_name">%1$s</xliff:g>
|
||||
is in use.</string>
|
||||
|
||||
<!-- Language Settings --> <skip />
|
||||
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings realted to locale and text -->
|
||||
|
@@ -59,6 +59,15 @@
|
||||
android:targetClass="com.android.settings.RunningServices" />
|
||||
</PreferenceScreen>
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="power_usage"
|
||||
android:title="@string/power_usage_summary_title"
|
||||
android:summary="@string/power_usage_summary">
|
||||
<intent android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.android.settings"
|
||||
android:targetClass="com.android.settings.fuelgauge.PowerUsageSummary" />
|
||||
</PreferenceScreen>
|
||||
|
||||
<PreferenceScreen
|
||||
android:title="@string/development_settings_title"
|
||||
android:summary="@string/development_settings_summary">
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,14 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings;
|
||||
package com.android.settings.applications;
|
||||
|
||||
import com.android.internal.content.PackageHelper;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.R.id;
|
||||
import com.android.settings.R.layout;
|
||||
import com.android.settings.R.string;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
@@ -312,7 +316,7 @@ public class InstalledAppDetails extends Activity implements View.OnClickListene
|
||||
|
||||
// Get application's name from intent
|
||||
Intent intent = getIntent();
|
||||
final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
|
||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||
if (! initAppInfo(packageName)) {
|
||||
return; // could not find package, finish called
|
||||
}
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings;
|
||||
package com.android.settings.applications;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -119,7 +120,6 @@ public class ManageApplications extends TabActivity implements
|
||||
private static final boolean DEBUG_TIME = false;
|
||||
|
||||
// attributes used as keys when passing values to InstalledAppDetails activity
|
||||
public static final String APP_PKG_NAME = "pkg";
|
||||
public static final String APP_CHG = "chg";
|
||||
|
||||
// attribute name used in receiver for tagging names of added/deleted packages
|
||||
@@ -140,9 +140,8 @@ public class ManageApplications extends TabActivity implements
|
||||
private static final int MENU_OPTIONS_BASE = 0;
|
||||
// Filter options used for displayed list of applications
|
||||
public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
|
||||
public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 1;
|
||||
public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 2;
|
||||
public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 3;
|
||||
public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
|
||||
public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
|
||||
|
||||
public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
|
||||
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
|
||||
@@ -220,6 +219,8 @@ public class ManageApplications extends TabActivity implements
|
||||
private boolean mSizesFirst = false;
|
||||
// ListView used to display list
|
||||
private ListView mListView;
|
||||
// Custom view used to display running processes
|
||||
private RunningProcessesView mRunningProcessesView;
|
||||
// State variables used to figure out menu options and also
|
||||
// initiate the first computation and loading of resources
|
||||
private boolean mJustCreated = true;
|
||||
@@ -227,6 +228,14 @@ public class ManageApplications extends TabActivity implements
|
||||
private long mLoadTimeStart;
|
||||
private boolean mSetListViewLater = true;
|
||||
|
||||
// These are for keeping track of activity and tab switch state.
|
||||
private int mCurView;
|
||||
private boolean mCreatedRunning;
|
||||
|
||||
private boolean mResumedRunning;
|
||||
private boolean mActivityResumed;
|
||||
private Object mNonConfigInstance;
|
||||
|
||||
/*
|
||||
* Handler class to handle messages for various operations.
|
||||
* Most of the operations that effect Application related data
|
||||
@@ -268,10 +277,8 @@ public class ManageApplications extends TabActivity implements
|
||||
boolean status;
|
||||
long size;
|
||||
String formattedSize;
|
||||
ApplicationInfo info;
|
||||
Bundle data;
|
||||
String pkgName = null;
|
||||
AppInfo appInfo;
|
||||
data = msg.getData();
|
||||
if(data != null) {
|
||||
pkgName = data.getString(ATTR_PKG_NAME);
|
||||
@@ -359,12 +366,6 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
break;
|
||||
}
|
||||
try {
|
||||
info = mPm.getApplicationInfo(pkgName, 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Couldnt find application info for:"+pkgName);
|
||||
break;
|
||||
}
|
||||
mObserver.invokeGetSizeInfo(pkgName);
|
||||
break;
|
||||
case ADD_PKG_DONE:
|
||||
@@ -456,7 +457,6 @@ public class ManageApplications extends TabActivity implements
|
||||
// Set the adapter here.
|
||||
mJustCreated = false;
|
||||
mListView.setAdapter(mAppInfoAdapter);
|
||||
dismissLoadingMsg();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,32 +645,6 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
}
|
||||
return appList;
|
||||
} else if (filterOption == FILTER_APPS_RUNNING) {
|
||||
List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
|
||||
List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
|
||||
if ((procList == null) || (procList.size() == 0)) {
|
||||
return appList;
|
||||
}
|
||||
// Retrieve running processes from ActivityManager
|
||||
for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
|
||||
if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
|
||||
int size = appProcInfo.pkgList.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
ApplicationInfo appInfo = null;
|
||||
try {
|
||||
appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
|
||||
continue;
|
||||
}
|
||||
if(appInfo != null) {
|
||||
appList.add(appInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return appList;
|
||||
} else {
|
||||
return installedAppList;
|
||||
}
|
||||
@@ -728,31 +702,6 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
}
|
||||
return retList;
|
||||
} else if (filterOption == FILTER_APPS_RUNNING) {
|
||||
List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
|
||||
if ((procList == null) || (procList.size() == 0)) {
|
||||
return retList;
|
||||
}
|
||||
// Retrieve running processes from ActivityManager
|
||||
HashMap<String, ActivityManager.RunningAppProcessInfo> runningMap =
|
||||
new HashMap<String, ActivityManager.RunningAppProcessInfo>();
|
||||
for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
|
||||
if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
|
||||
int size = appProcInfo.pkgList.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
runningMap.put(appProcInfo.pkgList[i], appProcInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Query list to find running processes in current list
|
||||
for (ApplicationInfo appInfo : pAppList) {
|
||||
if (runningMap.get(appInfo.packageName) != null) {
|
||||
if (matchFilter(filter, filterMap, appInfo.packageName)) {
|
||||
retList.add(appInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retList;
|
||||
} else {
|
||||
for (ApplicationInfo appInfo : pAppList) {
|
||||
if (matchFilter(filter, filterMap, appInfo.packageName)) {
|
||||
@@ -763,11 +712,6 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
|
||||
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
|
||||
return am.getRunningAppProcesses();
|
||||
}
|
||||
|
||||
// Some initialization code used when kicking off the size computation
|
||||
private void initAppList(List<ApplicationInfo> appList, int filterOption) {
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
@@ -1261,14 +1205,7 @@ public class ManageApplications extends TabActivity implements
|
||||
|
||||
private boolean shouldBeInList(int filterOption, ApplicationInfo info) {
|
||||
// Match filter here
|
||||
if (filterOption == FILTER_APPS_RUNNING) {
|
||||
List<ApplicationInfo> runningList = getInstalledApps(FILTER_APPS_RUNNING);
|
||||
for (ApplicationInfo running : runningList) {
|
||||
if (running.packageName.equalsIgnoreCase(info.packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (filterOption == FILTER_APPS_THIRD_PARTY) {
|
||||
if (filterOption == FILTER_APPS_THIRD_PARTY) {
|
||||
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
return true;
|
||||
} else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
|
||||
@@ -1611,11 +1548,44 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
static final int VIEW_NOTHING = 0;
|
||||
static final int VIEW_LIST = 1;
|
||||
static final int VIEW_RUNNING = 2;
|
||||
|
||||
private void selectView(int which) {
|
||||
if (mCurView == which) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCurView = which;
|
||||
|
||||
if (which == VIEW_LIST) {
|
||||
if (mResumedRunning) {
|
||||
mRunningProcessesView.doPause();
|
||||
mResumedRunning = false;
|
||||
}
|
||||
mRunningProcessesView.setVisibility(View.GONE);
|
||||
mListView.setVisibility(View.VISIBLE);
|
||||
} else if (which == VIEW_RUNNING) {
|
||||
if (!mCreatedRunning) {
|
||||
mRunningProcessesView.doCreate(null, mNonConfigInstance);
|
||||
mCreatedRunning = true;
|
||||
}
|
||||
if (mActivityResumed && !mResumedRunning) {
|
||||
mRunningProcessesView.doResume();
|
||||
mResumedRunning = true;
|
||||
}
|
||||
mRunningProcessesView.setVisibility(View.VISIBLE);
|
||||
mListView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
static final String TAB_DOWNLOADED = "Downloaded";
|
||||
static final String TAB_RUNNING = "Running";
|
||||
static final String TAB_ALL = "All";
|
||||
static final String TAB_SDCARD = "OnSdCard";
|
||||
private View mRootView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -1627,18 +1597,31 @@ public class ManageApplications extends TabActivity implements
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
String defaultTabTag = TAB_DOWNLOADED;
|
||||
if (intent.getComponent().getClassName().equals(
|
||||
"com.android.settings.RunningServices")) {
|
||||
defaultTabTag = TAB_RUNNING;
|
||||
}
|
||||
if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
|
||||
mSortOrder = SORT_ORDER_SIZE;
|
||||
mFilterApps = FILTER_APPS_ALL;
|
||||
defaultTabTag = TAB_ALL;
|
||||
mSizesFirst = true;
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder);
|
||||
mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
|
||||
String tmp = savedInstanceState.getString("defaultTabTag");
|
||||
if (tmp != null) defaultTabTag = tmp;
|
||||
mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst);
|
||||
}
|
||||
|
||||
mNonConfigInstance = getLastNonConfigurationInstance();
|
||||
|
||||
mPm = getPackageManager();
|
||||
// initialize some window features
|
||||
requestWindowFeature(Window.FEATURE_RIGHT_ICON);
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
showLoadingMsg();
|
||||
mDefaultAppIcon = Resources.getSystem().getDrawable(
|
||||
com.android.internal.R.drawable.sym_def_app_icon);
|
||||
mInvalidSizeStr = getText(R.string.invalid_size_value);
|
||||
@@ -1658,6 +1641,8 @@ public class ManageApplications extends TabActivity implements
|
||||
lv.setOnItemClickListener(this);
|
||||
lv.setTextFilterEnabled(true);
|
||||
mListView = lv;
|
||||
mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
|
||||
R.id.running_processes);
|
||||
if (DEBUG_TIME) {
|
||||
Log.i(TAG, "Total time in Activity.create:: " +
|
||||
(SystemClock.elapsedRealtime() - sCreate)+ " ms");
|
||||
@@ -1677,10 +1662,6 @@ public class ManageApplications extends TabActivity implements
|
||||
.setIndicator(getString(R.string.filter_apps_third_party),
|
||||
getResources().getDrawable(R.drawable.ic_tab_download))
|
||||
.setContent(this));
|
||||
tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
|
||||
.setIndicator(getString(R.string.filter_apps_running),
|
||||
getResources().getDrawable(R.drawable.ic_tab_running))
|
||||
.setContent(this));
|
||||
tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
|
||||
.setIndicator(getString(R.string.filter_apps_all),
|
||||
getResources().getDrawable(R.drawable.ic_tab_all))
|
||||
@@ -1689,8 +1670,95 @@ public class ManageApplications extends TabActivity implements
|
||||
.setIndicator(getString(R.string.filter_apps_onsdcard),
|
||||
getResources().getDrawable(R.drawable.ic_tab_sdcard))
|
||||
.setContent(this));
|
||||
tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
|
||||
.setIndicator(getString(R.string.filter_apps_running),
|
||||
getResources().getDrawable(R.drawable.ic_tab_running))
|
||||
.setContent(this));
|
||||
tabHost.setCurrentTabByTag(defaultTabTag);
|
||||
tabHost.setOnTabChangedListener(this);
|
||||
|
||||
selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Register receiver
|
||||
mReceiver.registerReceiver();
|
||||
sendMessageToHandler(INIT_PKG_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mActivityResumed = true;
|
||||
if (mCurView == VIEW_RUNNING) {
|
||||
mRunningProcessesView.doResume();
|
||||
mResumedRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("sortOrder", mSortOrder);
|
||||
outState.putInt("filterApps", mFilterApps);
|
||||
outState.putString("defautTabTag", getTabHost().getCurrentTabTag());
|
||||
outState.putBoolean("sizesFirst", mSizesFirst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return mRunningProcessesView.doRetainNonConfigurationInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mActivityResumed = false;
|
||||
if (mResumedRunning) {
|
||||
mRunningProcessesView.doPause();
|
||||
mResumedRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
// Stop the background threads
|
||||
if (mResourceThread != null) {
|
||||
mResourceThread.setAbort();
|
||||
}
|
||||
if (mSizeComputor != null) {
|
||||
mSizeComputor.setAbort();
|
||||
}
|
||||
// clear all messages related to application list
|
||||
clearMessagesInHandler();
|
||||
// register receiver here
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
|
||||
// Refresh package attributes
|
||||
try {
|
||||
ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
} catch (NameNotFoundException e) {
|
||||
Bundle rData = new Bundle();
|
||||
rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
|
||||
sendMessageToHandler(REMOVE_PKG, rData);
|
||||
mCurrentPkgName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid the restart and pause when orientation changes
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1700,34 +1768,6 @@ public class ManageApplications extends TabActivity implements
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int id, Bundle args) {
|
||||
if (id == DLG_LOADING) {
|
||||
ProgressDialog dlg = new ProgressDialog(this);
|
||||
dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
dlg.setMessage(getText(R.string.loading));
|
||||
dlg.setIndeterminate(true);
|
||||
dlg.setOnCancelListener(this);
|
||||
return dlg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void showLoadingMsg() {
|
||||
if (DEBUG_TIME) {
|
||||
mLoadTimeStart = SystemClock.elapsedRealtime();
|
||||
}
|
||||
showDialog(DLG_LOADING);
|
||||
if(localLOGV) Log.i(TAG, "Displaying Loading message");
|
||||
}
|
||||
|
||||
private void dismissLoadingMsg() {
|
||||
if(localLOGV) Log.i(TAG, "Dismissing Loading message");
|
||||
dismissDialog(DLG_LOADING);
|
||||
if (DEBUG_TIME) Log.i(TAG, "Displayed loading message for "+
|
||||
(SystemClock.elapsedRealtime() - mLoadTimeStart) + " ms");
|
||||
}
|
||||
|
||||
class AppInfoCache {
|
||||
final static boolean FILE_CACHE = true;
|
||||
private static final String mFileCacheName="ManageAppsInfo.txt";
|
||||
@@ -1923,36 +1963,6 @@ public class ManageApplications extends TabActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Register receiver
|
||||
mReceiver.registerReceiver();
|
||||
sendMessageToHandler(INIT_PKG_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
// Stop the background threads
|
||||
if (mResourceThread != null) {
|
||||
mResourceThread.setAbort();
|
||||
}
|
||||
if (mSizeComputor != null) {
|
||||
mSizeComputor.setAbort();
|
||||
}
|
||||
// clear all messages related to application list
|
||||
clearMessagesInHandler();
|
||||
// register receiver here
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
// Avoid the restart and pause when orientation changes
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
/*
|
||||
* comparator class used to sort AppInfo objects based on size
|
||||
*/
|
||||
@@ -1994,9 +2004,8 @@ public class ManageApplications extends TabActivity implements
|
||||
// utility method used to start sub activity
|
||||
private void startApplicationDetailsActivity() {
|
||||
// Create intent to start new activity
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClass(this, InstalledAppDetails.class);
|
||||
intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", mCurrentPkgName, null));
|
||||
// start new activity to display extended information
|
||||
startActivityForResult(intent, INSTALLED_APP_DETAILS);
|
||||
}
|
||||
@@ -2049,33 +2058,19 @@ public class ManageApplications extends TabActivity implements
|
||||
int newOption;
|
||||
if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
|
||||
newOption = FILTER_APPS_THIRD_PARTY;
|
||||
} else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
|
||||
newOption = FILTER_APPS_RUNNING;
|
||||
} else if (TAB_ALL.equalsIgnoreCase(tabId)) {
|
||||
newOption = FILTER_APPS_ALL;
|
||||
} else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
|
||||
newOption = FILTER_APPS_SDCARD;
|
||||
} else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
|
||||
selectView(VIEW_RUNNING);
|
||||
return;
|
||||
} else {
|
||||
// Invalid option. Do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
selectView(VIEW_LIST);
|
||||
sendMessageToHandler(REORDER_LIST, newOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
|
||||
// Refresh package attributes
|
||||
try {
|
||||
ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
} catch (NameNotFoundException e) {
|
||||
Bundle rData = new Bundle();
|
||||
rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
|
||||
sendMessageToHandler(REMOVE_PKG, rData);
|
||||
mCurrentPkgName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
548
src/com/android/settings/applications/RunningProcessesView.java
Normal file
548
src/com/android/settings/applications/RunningProcessesView.java
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class RunningProcessesView extends FrameLayout
|
||||
implements AdapterView.OnItemClickListener {
|
||||
|
||||
/** Maximum number of services to retrieve */
|
||||
static final int MAX_SERVICES = 100;
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
static final long TIME_UPDATE_DELAY = 1000;
|
||||
static final long CONTENTS_UPDATE_DELAY = 2000;
|
||||
|
||||
// Memory pages are 4K.
|
||||
static final long PAGE_SIZE = 4*1024;
|
||||
|
||||
long SECONDARY_SERVER_MEM;
|
||||
|
||||
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
|
||||
|
||||
ActivityManager mAm;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
RunningState.BaseItem mCurSelected;
|
||||
|
||||
ListView mListView;
|
||||
LinearColorBar mColorBar;
|
||||
TextView mBackgroundProcessText;
|
||||
TextView mForegroundProcessText;
|
||||
|
||||
int mLastNumBackgroundProcesses = -1;
|
||||
int mLastNumForegroundProcesses = -1;
|
||||
int mLastNumServiceProcesses = -1;
|
||||
long mLastBackgroundProcessMemory = -1;
|
||||
long mLastForegroundProcessMemory = -1;
|
||||
long mLastServiceProcessMemory = -1;
|
||||
long mLastAvailMemory = -1;
|
||||
|
||||
Dialog mCurDialog;
|
||||
|
||||
byte[] mBuffer = new byte[1024];
|
||||
|
||||
public static class ActiveItem {
|
||||
View mRootView;
|
||||
RunningState.BaseItem mItem;
|
||||
ActivityManager.RunningServiceInfo mService;
|
||||
ViewHolder mHolder;
|
||||
long mFirstRunTime;
|
||||
|
||||
void updateTime(Context context, StringBuilder builder) {
|
||||
TextView uptimeView = null;
|
||||
|
||||
if (mItem instanceof RunningState.ServiceItem) {
|
||||
// If we are displaying a service, then the service
|
||||
// uptime goes at the top.
|
||||
uptimeView = mHolder.size;
|
||||
|
||||
} else {
|
||||
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
|
||||
if (!size.equals(mItem.mCurSizeStr)) {
|
||||
mItem.mCurSizeStr = size;
|
||||
mHolder.size.setText(size);
|
||||
}
|
||||
|
||||
if (mItem instanceof RunningState.MergedItem) {
|
||||
// This item represents both services and proceses,
|
||||
// so show the service uptime below.
|
||||
uptimeView = mHolder.uptime;
|
||||
}
|
||||
}
|
||||
|
||||
if (uptimeView != null) {
|
||||
if (mItem.mActiveSince >= 0) {
|
||||
uptimeView.setText(DateUtils.formatElapsedTime(builder,
|
||||
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
|
||||
} else {
|
||||
uptimeView.setText(context.getResources().getText(
|
||||
R.string.service_restarting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ViewHolder {
|
||||
public View rootView;
|
||||
public ImageView icon;
|
||||
public TextView name;
|
||||
public TextView description;
|
||||
public TextView size;
|
||||
public TextView uptime;
|
||||
|
||||
public ViewHolder(View v) {
|
||||
rootView = v;
|
||||
icon = (ImageView)v.findViewById(R.id.icon);
|
||||
name = (TextView)v.findViewById(R.id.name);
|
||||
description = (TextView)v.findViewById(R.id.description);
|
||||
size = (TextView)v.findViewById(R.id.size);
|
||||
uptime = (TextView)v.findViewById(R.id.uptime);
|
||||
v.setTag(this);
|
||||
}
|
||||
|
||||
public ActiveItem bind(RunningState state, RunningState.BaseItem item,
|
||||
StringBuilder builder) {
|
||||
synchronized (state.mLock) {
|
||||
name.setText(item.mDisplayLabel);
|
||||
ActiveItem ai = new ActiveItem();
|
||||
ai.mRootView = rootView;
|
||||
ai.mItem = item;
|
||||
ai.mHolder = this;
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
description.setText(item.mDescription);
|
||||
item.mCurSizeStr = null;
|
||||
icon.setImageDrawable(item.mPackageInfo.loadIcon(
|
||||
rootView.getContext().getPackageManager()));
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
ai.updateTime(rootView.getContext(), builder);
|
||||
return ai;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TimeTicker extends TextView {
|
||||
public TimeTicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceListAdapter extends BaseAdapter {
|
||||
final RunningState mState;
|
||||
final LayoutInflater mInflater;
|
||||
ArrayList<RunningState.MergedItem> mItems;
|
||||
|
||||
ServiceListAdapter(RunningState state) {
|
||||
mState = state;
|
||||
mInflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
void refreshItems() {
|
||||
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
|
||||
if (mItems != newItems) {
|
||||
mItems = newItems;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new ArrayList<RunningState.MergedItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).hashCode();
|
||||
}
|
||||
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled(int position) {
|
||||
return !mItems.get(position).mIsProcess;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
|
||||
new ViewHolder(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
synchronized (mState.mLock) {
|
||||
if (position >= mItems.size()) {
|
||||
// List must have changed since we last reported its
|
||||
// size... ignore here, we will be doing a data changed
|
||||
// to refresh the entire list.
|
||||
return;
|
||||
}
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
RunningState.MergedItem item = mItems.get(position);
|
||||
ActiveItem ai = vh.bind(mState, item, mBuilder);
|
||||
mActiveItems.put(view, ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinearColorBar extends LinearLayout {
|
||||
private float mRedRatio;
|
||||
private float mYellowRatio;
|
||||
private float mGreenRatio;
|
||||
|
||||
final Rect mRect = new Rect();
|
||||
final Paint mPaint = new Paint();
|
||||
|
||||
public LinearColorBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
public void setRatios(float red, float yellow, float green) {
|
||||
mRedRatio = red;
|
||||
mYellowRatio = yellow;
|
||||
mGreenRatio = green;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
|
||||
int left = 0;
|
||||
|
||||
int right = left + (int)(width*mRedRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffff8080);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + (int)(width*mYellowRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffffff00);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + width;
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xff80ff80);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
Iterator<ActiveItem> it = mActiveItems.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ActiveItem ai = it.next();
|
||||
if (ai.mRootView.getWindowToken() == null) {
|
||||
// Clean out any dead views, just in case.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
ai.updateTime(getContext(), mBuilder);
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean matchText(byte[] buffer, int index, String text) {
|
||||
int N = text.length();
|
||||
if ((index+N) >= buffer.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<N; i++) {
|
||||
if (buffer[index+i] != text.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long extractMemValue(byte[] buffer, int index) {
|
||||
while (index < buffer.length && buffer[index] != '\n') {
|
||||
if (buffer[index] >= '0' && buffer[index] <= '9') {
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < buffer.length && buffer[index] >= '0'
|
||||
&& buffer[index] <= '9') {
|
||||
index++;
|
||||
}
|
||||
String str = new String(buffer, 0, start, index-start);
|
||||
return ((long)Integer.parseInt(str)) * 1024;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long readAvailMem() {
|
||||
try {
|
||||
long memFree = 0;
|
||||
long memCached = 0;
|
||||
FileInputStream is = new FileInputStream("/proc/meminfo");
|
||||
int len = is.read(mBuffer);
|
||||
is.close();
|
||||
final int BUFLEN = mBuffer.length;
|
||||
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
|
||||
if (matchText(mBuffer, i, "MemFree")) {
|
||||
i += 7;
|
||||
memFree = extractMemValue(mBuffer, i);
|
||||
} else if (matchText(mBuffer, i, "Cached")) {
|
||||
i += 6;
|
||||
memCached = extractMemValue(mBuffer, i);
|
||||
}
|
||||
while (i < BUFLEN && mBuffer[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return memFree + memCached;
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (dataChanged) {
|
||||
ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
|
||||
adapter.refreshItems();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// This is the amount of available memory until we start killing
|
||||
// background services.
|
||||
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
|
||||
if (availMem < 0) {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
synchronized (mState.mLock) {
|
||||
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|
||||
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|
||||
|| mLastAvailMemory != availMem) {
|
||||
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
|
||||
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
|
||||
mLastAvailMemory = availMem;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastAvailMemory + mLastBackgroundProcessMemory);
|
||||
mBackgroundProcessText.setText(getResources().getString(
|
||||
R.string.service_background_processes, sizeStr));
|
||||
}
|
||||
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|
||||
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory
|
||||
|| mLastNumServiceProcesses != mState.mNumServiceProcesses
|
||||
|| mLastServiceProcessMemory != mState.mServiceProcessMemory) {
|
||||
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
|
||||
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
|
||||
mLastNumServiceProcesses = mState.mNumServiceProcesses;
|
||||
mLastServiceProcessMemory = mState.mServiceProcessMemory;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastForegroundProcessMemory + mLastServiceProcessMemory);
|
||||
mForegroundProcessText.setText(getResources().getString(
|
||||
R.string.service_foreground_processes, sizeStr));
|
||||
}
|
||||
|
||||
float totalMem = availMem + mLastBackgroundProcessMemory
|
||||
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
|
||||
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
|
||||
mLastServiceProcessMemory/totalMem,
|
||||
(availMem+mLastBackgroundProcessMemory)/totalMem);
|
||||
}
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
ListView l = (ListView)parent;
|
||||
RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
|
||||
mCurSelected = mi;
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
|
||||
intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
|
||||
intent.setClass(getContext(), RunningServiceDetails.class);
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
mActiveItems.remove(view);
|
||||
}
|
||||
|
||||
public RunningProcessesView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
|
||||
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mState = (RunningState)nonConfigurationInstace;
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.running_processes_view, this);
|
||||
mListView = (ListView)findViewById(android.R.id.list);
|
||||
View emptyView = findViewById(com.android.internal.R.id.empty);
|
||||
if (emptyView != null) {
|
||||
mListView.setEmptyView(emptyView);
|
||||
}
|
||||
mListView.setOnItemClickListener(this);
|
||||
mListView.setAdapter(new ServiceListAdapter(mState));
|
||||
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
|
||||
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
|
||||
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
|
||||
|
||||
// Magic! Implementation detail! Don't count on this!
|
||||
SECONDARY_SERVER_MEM =
|
||||
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
public void doPause() {
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void doResume() {
|
||||
refreshUi(mState.update(getContext(), mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
public Object doRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
}
|
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class RunningProcessesViewOld extends FrameLayout
|
||||
implements AdapterView.OnItemClickListener {
|
||||
|
||||
static final String TAG = "RunningServices";
|
||||
|
||||
/** Maximum number of services to retrieve */
|
||||
static final int MAX_SERVICES = 100;
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
static final long TIME_UPDATE_DELAY = 1000;
|
||||
static final long CONTENTS_UPDATE_DELAY = 2000;
|
||||
|
||||
// Memory pages are 4K.
|
||||
static final long PAGE_SIZE = 4*1024;
|
||||
|
||||
long SECONDARY_SERVER_MEM;
|
||||
|
||||
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
|
||||
|
||||
ActivityManager mAm;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
RunningState.BaseItem mCurSelected;
|
||||
|
||||
int mProcessBgColor;
|
||||
|
||||
ListView mListView;
|
||||
LinearColorBar mColorBar;
|
||||
TextView mBackgroundProcessText;
|
||||
TextView mForegroundProcessText;
|
||||
|
||||
int mLastNumBackgroundProcesses = -1;
|
||||
int mLastNumForegroundProcesses = -1;
|
||||
int mLastNumServiceProcesses = -1;
|
||||
long mLastBackgroundProcessMemory = -1;
|
||||
long mLastForegroundProcessMemory = -1;
|
||||
long mLastServiceProcessMemory = -1;
|
||||
long mLastAvailMemory = -1;
|
||||
|
||||
Dialog mCurDialog;
|
||||
|
||||
byte[] mBuffer = new byte[1024];
|
||||
|
||||
class ActiveItem {
|
||||
View mRootView;
|
||||
RunningState.BaseItem mItem;
|
||||
ActivityManager.RunningServiceInfo mService;
|
||||
ViewHolder mHolder;
|
||||
long mFirstRunTime;
|
||||
|
||||
void updateTime(Context context) {
|
||||
if (mItem.mIsProcess) {
|
||||
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
|
||||
if (!size.equals(mItem.mCurSizeStr)) {
|
||||
mItem.mCurSizeStr = size;
|
||||
mHolder.size.setText(size);
|
||||
}
|
||||
} else {
|
||||
if (mItem.mActiveSince >= 0) {
|
||||
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
|
||||
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
|
||||
} else {
|
||||
mHolder.size.setText(context.getResources().getText(
|
||||
R.string.service_restarting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView separator;
|
||||
ImageView icon;
|
||||
TextView name;
|
||||
TextView description;
|
||||
TextView size;
|
||||
}
|
||||
|
||||
static class TimeTicker extends TextView {
|
||||
public TimeTicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceListAdapter extends BaseAdapter {
|
||||
final RunningState mState;
|
||||
final LayoutInflater mInflater;
|
||||
ArrayList<RunningState.BaseItem> mItems;
|
||||
|
||||
ServiceListAdapter(RunningState state) {
|
||||
mState = state;
|
||||
mInflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
void refreshItems() {
|
||||
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
|
||||
if (mItems != newItems) {
|
||||
mItems = newItems;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new ArrayList<RunningState.BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).hashCode();
|
||||
}
|
||||
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled(int position) {
|
||||
return !mItems.get(position).mIsProcess;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.separator = (ImageView)v.findViewById(R.id.separator);
|
||||
h.icon = (ImageView)v.findViewById(R.id.icon);
|
||||
h.name = (TextView)v.findViewById(R.id.name);
|
||||
h.description = (TextView)v.findViewById(R.id.description);
|
||||
h.size = (TextView)v.findViewById(R.id.size);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
synchronized (mState.mLock) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
if (position >= mItems.size()) {
|
||||
// List must have changed since we last reported its
|
||||
// size... ignore here, we will be doing a data changed
|
||||
// to refresh the entire list.
|
||||
return;
|
||||
}
|
||||
RunningState.BaseItem item = mItems.get(position);
|
||||
vh.name.setText(item.mDisplayLabel);
|
||||
vh.separator.setVisibility(item.mNeedDivider
|
||||
? View.VISIBLE : View.INVISIBLE);
|
||||
ActiveItem ai = new ActiveItem();
|
||||
ai.mRootView = view;
|
||||
ai.mItem = item;
|
||||
ai.mHolder = vh;
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
vh.description.setText(item.mDescription);
|
||||
if (item.mIsProcess) {
|
||||
view.setBackgroundColor(mProcessBgColor);
|
||||
vh.icon.setImageDrawable(null);
|
||||
vh.icon.setVisibility(View.GONE);
|
||||
vh.description.setText(item.mDescription);
|
||||
item.mCurSizeStr = null;
|
||||
} else {
|
||||
view.setBackgroundDrawable(null);
|
||||
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(
|
||||
getContext().getPackageManager()));
|
||||
vh.icon.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(item.mDescription);
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
}
|
||||
ai.updateTime(getContext());
|
||||
mActiveItems.put(view, ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinearColorBar extends LinearLayout {
|
||||
private float mRedRatio;
|
||||
private float mYellowRatio;
|
||||
private float mGreenRatio;
|
||||
|
||||
final Rect mRect = new Rect();
|
||||
final Paint mPaint = new Paint();
|
||||
|
||||
public LinearColorBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
public void setRatios(float red, float yellow, float green) {
|
||||
mRedRatio = red;
|
||||
mYellowRatio = yellow;
|
||||
mGreenRatio = green;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
|
||||
int left = 0;
|
||||
|
||||
int right = left + (int)(width*mRedRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffff8080);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + (int)(width*mYellowRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffffff00);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + width;
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xff80ff80);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
Iterator<ActiveItem> it = mActiveItems.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ActiveItem ai = it.next();
|
||||
if (ai.mRootView.getWindowToken() == null) {
|
||||
// Clean out any dead views, just in case.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
ai.updateTime(getContext());
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean matchText(byte[] buffer, int index, String text) {
|
||||
int N = text.length();
|
||||
if ((index+N) >= buffer.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<N; i++) {
|
||||
if (buffer[index+i] != text.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long extractMemValue(byte[] buffer, int index) {
|
||||
while (index < buffer.length && buffer[index] != '\n') {
|
||||
if (buffer[index] >= '0' && buffer[index] <= '9') {
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < buffer.length && buffer[index] >= '0'
|
||||
&& buffer[index] <= '9') {
|
||||
index++;
|
||||
}
|
||||
String str = new String(buffer, 0, start, index-start);
|
||||
return ((long)Integer.parseInt(str)) * 1024;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long readAvailMem() {
|
||||
try {
|
||||
long memFree = 0;
|
||||
long memCached = 0;
|
||||
FileInputStream is = new FileInputStream("/proc/meminfo");
|
||||
int len = is.read(mBuffer);
|
||||
is.close();
|
||||
final int BUFLEN = mBuffer.length;
|
||||
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
|
||||
if (matchText(mBuffer, i, "MemFree")) {
|
||||
i += 7;
|
||||
memFree = extractMemValue(mBuffer, i);
|
||||
} else if (matchText(mBuffer, i, "Cached")) {
|
||||
i += 6;
|
||||
memCached = extractMemValue(mBuffer, i);
|
||||
}
|
||||
while (i < BUFLEN && mBuffer[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return memFree + memCached;
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (dataChanged) {
|
||||
ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
|
||||
adapter.refreshItems();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// This is the amount of available memory until we start killing
|
||||
// background services.
|
||||
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
|
||||
if (availMem < 0) {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
synchronized (mState.mLock) {
|
||||
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|
||||
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|
||||
|| mLastAvailMemory != availMem) {
|
||||
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
|
||||
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
|
||||
mLastAvailMemory = availMem;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastAvailMemory + mLastBackgroundProcessMemory);
|
||||
mBackgroundProcessText.setText(getResources().getString(
|
||||
R.string.service_background_processes, sizeStr));
|
||||
}
|
||||
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|
||||
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
|
||||
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
|
||||
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
|
||||
String sizeStr = Formatter.formatShortFileSize(getContext(),
|
||||
mLastForegroundProcessMemory);
|
||||
mForegroundProcessText.setText(getResources().getString(
|
||||
R.string.service_foreground_processes, sizeStr));
|
||||
}
|
||||
mLastNumServiceProcesses = mState.mNumServiceProcesses;
|
||||
mLastServiceProcessMemory = mState.mServiceProcessMemory;
|
||||
|
||||
float totalMem = availMem + mLastBackgroundProcessMemory
|
||||
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
|
||||
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
|
||||
mLastServiceProcessMemory/totalMem,
|
||||
(availMem+mLastBackgroundProcessMemory)/totalMem);
|
||||
}
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
ListView l = (ListView)parent;
|
||||
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
|
||||
mCurSelected = bi;
|
||||
}
|
||||
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
mActiveItems.remove(view);
|
||||
}
|
||||
|
||||
public RunningProcessesViewOld(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
|
||||
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mState = (RunningState)nonConfigurationInstace;
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
mProcessBgColor = 0xff505050;
|
||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.running_processes_view, this);
|
||||
mListView = (ListView)findViewById(android.R.id.list);
|
||||
View emptyView = findViewById(com.android.internal.R.id.empty);
|
||||
if (emptyView != null) {
|
||||
mListView.setEmptyView(emptyView);
|
||||
}
|
||||
mListView.setOnItemClickListener(this);
|
||||
mListView.setDivider(null);
|
||||
mListView.setAdapter(new ServiceListAdapter(mState));
|
||||
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
|
||||
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
|
||||
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
|
||||
|
||||
// Magic! Implementation detail! Don't count on this!
|
||||
SECONDARY_SERVER_MEM =
|
||||
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
public void doPause() {
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void doResume() {
|
||||
refreshUi(mState.update(getContext(), mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
public Object doRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
}
|
390
src/com/android/settings/applications/RunningServiceDetails.java
Normal file
390
src/com/android/settings/applications/RunningServiceDetails.java
Normal file
@@ -0,0 +1,390 @@
|
||||
package com.android.settings.applications;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RunningServiceDetails extends Activity {
|
||||
static final String TAG = "RunningServicesDetails";
|
||||
|
||||
static final String KEY_UID = "uid";
|
||||
static final String KEY_PROCESS = "process";
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
ActivityManager mAm;
|
||||
LayoutInflater mInflater;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
int mUid;
|
||||
String mProcessName;
|
||||
|
||||
RunningState.MergedItem mMergedItem;
|
||||
|
||||
ViewGroup mAllDetails;
|
||||
ViewGroup mSnippet;
|
||||
RunningProcessesView.ActiveItem mSnippetActiveItem;
|
||||
RunningProcessesView.ViewHolder mSnippetViewHolder;
|
||||
|
||||
TextView mServicesHeader;
|
||||
TextView mProcessesHeader;
|
||||
final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
|
||||
|
||||
class ActiveDetail implements View.OnClickListener {
|
||||
View mRootView;
|
||||
RunningProcessesView.ActiveItem mActiveItem;
|
||||
RunningProcessesView.ViewHolder mViewHolder;
|
||||
PendingIntent mManageIntent;
|
||||
|
||||
public void onClick(View v) {
|
||||
if (mManageIntent != null) {
|
||||
try {
|
||||
startIntentSender(mManageIntent.getIntentSender(), null,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
|
||||
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
} else {
|
||||
RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
|
||||
stopService(new Intent().setComponent(si.mRunningService.service));
|
||||
if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
|
||||
// If there was only one service, we are finishing it,
|
||||
// so no reason for the UI to stick around.
|
||||
finish();
|
||||
} else {
|
||||
if (mBackgroundHandler != null) {
|
||||
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
if (mSnippetActiveItem != null) {
|
||||
mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
|
||||
}
|
||||
for (int i=0; i<mActiveDetails.size(); i++) {
|
||||
mActiveDetails.get(i).mActiveItem.updateTime(
|
||||
RunningServiceDetails.this, mBuilder);
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boolean findMergedItem() {
|
||||
RunningState.MergedItem item = null;
|
||||
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
|
||||
if (newItems != null) {
|
||||
for (int i=0; i<newItems.size(); i++) {
|
||||
RunningState.MergedItem mi = newItems.get(i);
|
||||
if (mi.mProcess.mUid == mUid
|
||||
&& mi.mProcess.mProcessName.equals(mProcessName)) {
|
||||
item = mi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mMergedItem != item) {
|
||||
mMergedItem = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void addDetailViews() {
|
||||
for (int i=mActiveDetails.size()-1; i>=0; i--) {
|
||||
mAllDetails.removeView(mActiveDetails.get(i).mRootView);
|
||||
}
|
||||
mActiveDetails.clear();
|
||||
|
||||
if (mServicesHeader != null) {
|
||||
mAllDetails.removeView(mServicesHeader);
|
||||
mServicesHeader = null;
|
||||
}
|
||||
|
||||
if (mProcessesHeader != null) {
|
||||
mAllDetails.removeView(mProcessesHeader);
|
||||
mProcessesHeader = null;
|
||||
}
|
||||
|
||||
if (mMergedItem != null) {
|
||||
for (int i=0; i<mMergedItem.mServices.size(); i++) {
|
||||
if (i == 0) {
|
||||
mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
|
||||
mAllDetails, false);
|
||||
mServicesHeader.setText(R.string.runningservicedetails_services_title);
|
||||
mAllDetails.addView(mServicesHeader);
|
||||
}
|
||||
RunningState.ServiceItem si = mMergedItem.mServices.get(i);
|
||||
ActiveDetail detail = new ActiveDetail();
|
||||
View root = mInflater.inflate(R.layout.running_service_details_service,
|
||||
mAllDetails, false);
|
||||
mAllDetails.addView(root);
|
||||
detail.mRootView = root;
|
||||
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
|
||||
detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
|
||||
|
||||
if (si.mRunningService.clientLabel != 0) {
|
||||
detail.mManageIntent = mAm.getRunningServiceControlPanel(
|
||||
si.mRunningService.service);
|
||||
}
|
||||
|
||||
TextView description = (TextView)root.findViewById(R.id.comp_description);
|
||||
if (si.mServiceInfo.descriptionRes != 0) {
|
||||
description.setText(getPackageManager().getText(
|
||||
si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
|
||||
si.mServiceInfo.applicationInfo));
|
||||
} else {
|
||||
if (detail.mManageIntent != null) {
|
||||
try {
|
||||
Resources clientr = getPackageManager().getResourcesForApplication(
|
||||
si.mRunningService.clientPackage);
|
||||
String label = clientr.getString(si.mRunningService.clientLabel);
|
||||
description.setText(getString(R.string.service_manage_description,
|
||||
label));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
} else {
|
||||
description.setText(getText(R.string.service_stop_description));
|
||||
}
|
||||
}
|
||||
|
||||
View button = root.findViewById(R.id.right_button);
|
||||
button.setOnClickListener(detail);
|
||||
((TextView)button).setText(getText(detail.mManageIntent != null
|
||||
? R.string.service_manage : R.string.service_stop));
|
||||
root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
|
||||
|
||||
mActiveDetails.add(detail);
|
||||
}
|
||||
|
||||
boolean didProcess = false;
|
||||
for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
|
||||
RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
|
||||
: mMergedItem.mOtherProcesses.get(i);
|
||||
if (pi.mPid <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!didProcess) {
|
||||
mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
|
||||
mAllDetails, false);
|
||||
mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
|
||||
mAllDetails.addView(mProcessesHeader);
|
||||
didProcess = true;
|
||||
}
|
||||
ActiveDetail detail = new ActiveDetail();
|
||||
View root = mInflater.inflate(R.layout.running_service_details_process,
|
||||
mAllDetails, false);
|
||||
mAllDetails.addView(root);
|
||||
detail.mRootView = root;
|
||||
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
|
||||
detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
|
||||
|
||||
TextView description = (TextView)root.findViewById(R.id.comp_description);
|
||||
if (i < 0) {
|
||||
description.setText(R.string.main_running_process_description);
|
||||
} else {
|
||||
int textid = 0;
|
||||
CharSequence label = null;
|
||||
ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
|
||||
final ComponentName comp = rpi.importanceReasonComponent;
|
||||
//Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
|
||||
// + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
|
||||
switch (rpi.importanceReasonCode) {
|
||||
case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
|
||||
textid = R.string.process_provider_in_use_description;
|
||||
List<ProviderInfo> providers = null;
|
||||
if (comp != null) {
|
||||
providers = getPackageManager()
|
||||
.queryContentProviders(comp.getPackageName(),
|
||||
rpi.uid, 0);
|
||||
}
|
||||
if (providers != null) {
|
||||
for (int j=0; j<providers.size(); j++) {
|
||||
ProviderInfo prov = providers.get(i);
|
||||
if (comp.getClassName().equals(prov.name)) {
|
||||
label = RunningState.makeLabel(getPackageManager(),
|
||||
prov.name, prov);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
|
||||
textid = R.string.process_service_in_use_description;
|
||||
if (rpi.importanceReasonComponent != null) {
|
||||
try {
|
||||
ServiceInfo serv = getPackageManager().getServiceInfo(
|
||||
rpi.importanceReasonComponent, 0);
|
||||
label = RunningState.makeLabel(getPackageManager(),
|
||||
serv.name, serv);
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (textid != 0 && label != null) {
|
||||
description.setText(getString(textid, label));
|
||||
}
|
||||
}
|
||||
|
||||
mActiveDetails.add(detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (findMergedItem()) {
|
||||
dataChanged = true;
|
||||
}
|
||||
if (dataChanged) {
|
||||
if (mMergedItem != null) {
|
||||
mSnippetActiveItem = mSnippetViewHolder.bind(mState,
|
||||
mMergedItem, mBuilder);
|
||||
} else if (mSnippetActiveItem != null) {
|
||||
// Clear whatever is currently being shown.
|
||||
mSnippetActiveItem.mHolder.size.setText("");
|
||||
mSnippetActiveItem.mHolder.uptime.setText("");
|
||||
mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
|
||||
} else {
|
||||
// No merged item, never had one. Nothing to do.
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
addDetailViews();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mUid = getIntent().getIntExtra(KEY_UID, 0);
|
||||
mProcessName = getIntent().getStringExtra(KEY_PROCESS);
|
||||
|
||||
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
mState = (RunningState)getLastNonConfigurationInstance();
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
|
||||
setContentView(R.layout.running_service_details);
|
||||
|
||||
mAllDetails = (ViewGroup)findViewById(R.id.all_details);
|
||||
mSnippet = (ViewGroup)findViewById(R.id.snippet);
|
||||
mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
|
||||
mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
|
||||
mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
refreshUi(mState.update(this, mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
}
|
582
src/com/android/settings/applications/RunningServices.java
Normal file
582
src/com/android/settings/applications/RunningServices.java
Normal file
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ListActivity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class RunningServices extends ListActivity
|
||||
implements AbsListView.RecyclerListener,
|
||||
DialogInterface.OnClickListener {
|
||||
static final String TAG = "RunningServices";
|
||||
|
||||
/** Maximum number of services to retrieve */
|
||||
static final int MAX_SERVICES = 100;
|
||||
|
||||
static final int MSG_UPDATE_TIMES = 1;
|
||||
static final int MSG_UPDATE_CONTENTS = 2;
|
||||
static final int MSG_REFRESH_UI = 3;
|
||||
|
||||
static final long TIME_UPDATE_DELAY = 1000;
|
||||
static final long CONTENTS_UPDATE_DELAY = 2000;
|
||||
|
||||
// Memory pages are 4K.
|
||||
static final long PAGE_SIZE = 4*1024;
|
||||
|
||||
long SECONDARY_SERVER_MEM;
|
||||
|
||||
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
|
||||
|
||||
ActivityManager mAm;
|
||||
|
||||
RunningState mState;
|
||||
|
||||
StringBuilder mBuilder = new StringBuilder(128);
|
||||
|
||||
RunningState.BaseItem mCurSelected;
|
||||
|
||||
int mProcessBgColor;
|
||||
|
||||
LinearColorBar mColorBar;
|
||||
TextView mBackgroundProcessText;
|
||||
TextView mForegroundProcessText;
|
||||
|
||||
int mLastNumBackgroundProcesses = -1;
|
||||
int mLastNumForegroundProcesses = -1;
|
||||
int mLastNumServiceProcesses = -1;
|
||||
long mLastBackgroundProcessMemory = -1;
|
||||
long mLastForegroundProcessMemory = -1;
|
||||
long mLastServiceProcessMemory = -1;
|
||||
long mLastAvailMemory = -1;
|
||||
|
||||
Dialog mCurDialog;
|
||||
|
||||
byte[] mBuffer = new byte[1024];
|
||||
|
||||
class ActiveItem {
|
||||
View mRootView;
|
||||
RunningState.BaseItem mItem;
|
||||
ActivityManager.RunningServiceInfo mService;
|
||||
ViewHolder mHolder;
|
||||
long mFirstRunTime;
|
||||
|
||||
void updateTime(Context context) {
|
||||
if (mItem.mIsProcess) {
|
||||
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
|
||||
if (!size.equals(mItem.mCurSizeStr)) {
|
||||
mItem.mCurSizeStr = size;
|
||||
mHolder.size.setText(size);
|
||||
}
|
||||
} else {
|
||||
if (mItem.mActiveSince >= 0) {
|
||||
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
|
||||
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
|
||||
} else {
|
||||
mHolder.size.setText(context.getResources().getText(
|
||||
R.string.service_restarting));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView separator;
|
||||
ImageView icon;
|
||||
TextView name;
|
||||
TextView description;
|
||||
TextView size;
|
||||
}
|
||||
|
||||
static class TimeTicker extends TextView {
|
||||
public TimeTicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceListAdapter extends BaseAdapter {
|
||||
final RunningState mState;
|
||||
final LayoutInflater mInflater;
|
||||
ArrayList<RunningState.BaseItem> mItems;
|
||||
|
||||
ServiceListAdapter(RunningState state) {
|
||||
mState = state;
|
||||
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
void refreshItems() {
|
||||
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
|
||||
if (mItems != newItems) {
|
||||
mItems = newItems;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new ArrayList<RunningState.BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).hashCode();
|
||||
}
|
||||
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled(int position) {
|
||||
return !mItems.get(position).mIsProcess;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.separator = (ImageView)v.findViewById(R.id.separator);
|
||||
h.icon = (ImageView)v.findViewById(R.id.icon);
|
||||
h.name = (TextView)v.findViewById(R.id.name);
|
||||
h.description = (TextView)v.findViewById(R.id.description);
|
||||
h.size = (TextView)v.findViewById(R.id.size);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
synchronized (mState.mLock) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
if (position >= mItems.size()) {
|
||||
// List must have changed since we last reported its
|
||||
// size... ignore here, we will be doing a data changed
|
||||
// to refresh the entire list.
|
||||
return;
|
||||
}
|
||||
RunningState.BaseItem item = mItems.get(position);
|
||||
vh.name.setText(item.mDisplayLabel);
|
||||
vh.separator.setVisibility(item.mNeedDivider
|
||||
? View.VISIBLE : View.INVISIBLE);
|
||||
ActiveItem ai = new ActiveItem();
|
||||
ai.mRootView = view;
|
||||
ai.mItem = item;
|
||||
ai.mHolder = vh;
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
vh.description.setText(item.mDescription);
|
||||
if (item.mIsProcess) {
|
||||
view.setBackgroundColor(mProcessBgColor);
|
||||
vh.icon.setImageDrawable(null);
|
||||
vh.icon.setVisibility(View.GONE);
|
||||
vh.description.setText(item.mDescription);
|
||||
item.mCurSizeStr = null;
|
||||
} else {
|
||||
view.setBackgroundDrawable(null);
|
||||
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
|
||||
vh.icon.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(item.mDescription);
|
||||
ai.mFirstRunTime = item.mActiveSince;
|
||||
}
|
||||
ai.updateTime(RunningServices.this);
|
||||
mActiveItems.put(view, ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinearColorBar extends LinearLayout {
|
||||
private float mRedRatio;
|
||||
private float mYellowRatio;
|
||||
private float mGreenRatio;
|
||||
|
||||
final Rect mRect = new Rect();
|
||||
final Paint mPaint = new Paint();
|
||||
|
||||
public LinearColorBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
public void setRatios(float red, float yellow, float green) {
|
||||
mRedRatio = red;
|
||||
mYellowRatio = yellow;
|
||||
mGreenRatio = green;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
|
||||
int left = 0;
|
||||
|
||||
int right = left + (int)(width*mRedRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffff8080);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + (int)(width*mYellowRatio);
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xffffff00);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
width -= (right-left);
|
||||
left = right;
|
||||
}
|
||||
|
||||
right = left + width;
|
||||
if (left < right) {
|
||||
mRect.left = left;
|
||||
mRect.right = right;
|
||||
mPaint.setColor(0xff80ff80);
|
||||
canvas.drawRect(mRect, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandlerThread mBackgroundThread;
|
||||
final class BackgroundHandler extends Handler {
|
||||
public BackgroundHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_CONTENTS:
|
||||
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
|
||||
cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
|
||||
mHandler.sendMessage(cmd);
|
||||
removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundHandler mBackgroundHandler;
|
||||
|
||||
final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_TIMES:
|
||||
Iterator<ActiveItem> it = mActiveItems.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ActiveItem ai = it.next();
|
||||
if (ai.mRootView.getWindowToken() == null) {
|
||||
// Clean out any dead views, just in case.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
ai.updateTime(RunningServices.this);
|
||||
}
|
||||
removeMessages(MSG_UPDATE_TIMES);
|
||||
msg = obtainMessage(MSG_UPDATE_TIMES);
|
||||
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
break;
|
||||
case MSG_REFRESH_UI:
|
||||
refreshUi(msg.arg1 != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean matchText(byte[] buffer, int index, String text) {
|
||||
int N = text.length();
|
||||
if ((index+N) >= buffer.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<N; i++) {
|
||||
if (buffer[index+i] != text.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long extractMemValue(byte[] buffer, int index) {
|
||||
while (index < buffer.length && buffer[index] != '\n') {
|
||||
if (buffer[index] >= '0' && buffer[index] <= '9') {
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < buffer.length && buffer[index] >= '0'
|
||||
&& buffer[index] <= '9') {
|
||||
index++;
|
||||
}
|
||||
String str = new String(buffer, 0, start, index-start);
|
||||
return ((long)Integer.parseInt(str)) * 1024;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long readAvailMem() {
|
||||
try {
|
||||
long memFree = 0;
|
||||
long memCached = 0;
|
||||
FileInputStream is = new FileInputStream("/proc/meminfo");
|
||||
int len = is.read(mBuffer);
|
||||
is.close();
|
||||
final int BUFLEN = mBuffer.length;
|
||||
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
|
||||
if (matchText(mBuffer, i, "MemFree")) {
|
||||
i += 7;
|
||||
memFree = extractMemValue(mBuffer, i);
|
||||
} else if (matchText(mBuffer, i, "Cached")) {
|
||||
i += 6;
|
||||
memCached = extractMemValue(mBuffer, i);
|
||||
}
|
||||
while (i < BUFLEN && mBuffer[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return memFree + memCached;
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
|
||||
mState = (RunningState)getLastNonConfigurationInstance();
|
||||
if (mState == null) {
|
||||
mState = new RunningState();
|
||||
}
|
||||
mProcessBgColor = 0xff505050;
|
||||
setContentView(R.layout.running_services);
|
||||
getListView().setDivider(null);
|
||||
getListView().setAdapter(new ServiceListAdapter(mState));
|
||||
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
|
||||
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
|
||||
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
|
||||
|
||||
// Magic! Implementation detail! Don't count on this!
|
||||
SECONDARY_SERVER_MEM =
|
||||
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
void refreshUi(boolean dataChanged) {
|
||||
if (dataChanged) {
|
||||
ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
|
||||
adapter.refreshItems();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// This is the amount of available memory until we start killing
|
||||
// background services.
|
||||
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
|
||||
if (availMem < 0) {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
synchronized (mState.mLock) {
|
||||
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|
||||
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|
||||
|| mLastAvailMemory != availMem) {
|
||||
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
|
||||
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
|
||||
mLastAvailMemory = availMem;
|
||||
String availStr = availMem != 0
|
||||
? Formatter.formatShortFileSize(this, availMem) : "0";
|
||||
String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
|
||||
mBackgroundProcessText.setText(getResources().getString(
|
||||
R.string.service_background_processes,
|
||||
mLastNumBackgroundProcesses, availStr, sizeStr));
|
||||
}
|
||||
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|
||||
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
|
||||
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
|
||||
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
|
||||
String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
|
||||
mForegroundProcessText.setText(getResources().getString(
|
||||
R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
|
||||
}
|
||||
mLastNumServiceProcesses = mState.mNumServiceProcesses;
|
||||
mLastServiceProcessMemory = mState.mServiceProcessMemory;
|
||||
|
||||
float totalMem = availMem + mLastBackgroundProcessMemory
|
||||
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
|
||||
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
|
||||
mLastServiceProcessMemory/totalMem,
|
||||
(availMem+mLastBackgroundProcessMemory)/totalMem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
|
||||
if (!bi.mIsProcess) {
|
||||
RunningState.ServiceItem si = (RunningState.ServiceItem)bi;
|
||||
if (si.mRunningService.clientLabel != 0) {
|
||||
mCurSelected = null;
|
||||
PendingIntent pi = mAm.getRunningServiceControlPanel(
|
||||
si.mRunningService.service);
|
||||
if (pi != null) {
|
||||
try {
|
||||
this.startIntentSender(pi.getIntentSender(), null,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
|
||||
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mCurSelected = bi;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.confirm_stop_service);
|
||||
String msg = getResources().getString(
|
||||
R.string.confirm_stop_service_msg,
|
||||
si.mPackageInfo.loadLabel(getPackageManager()));
|
||||
builder.setMessage(msg);
|
||||
builder.setPositiveButton(R.string.confirm_stop_stop, this);
|
||||
builder.setNegativeButton(R.string.confirm_stop_cancel, null);
|
||||
builder.setCancelable(true);
|
||||
mCurDialog = builder.show();
|
||||
}
|
||||
} else {
|
||||
mCurSelected = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mCurSelected != null) {
|
||||
stopService(new Intent().setComponent(
|
||||
((RunningState.ServiceItem)mCurSelected).mRunningService.service));
|
||||
if (mBackgroundHandler != null) {
|
||||
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
if (mBackgroundThread != null) {
|
||||
mBackgroundThread.quit();
|
||||
mBackgroundThread = null;
|
||||
mBackgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
refreshUi(mState.update(this, mAm));
|
||||
mBackgroundThread = new HandlerThread("RunningServices");
|
||||
mBackgroundThread.start();
|
||||
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
|
||||
mHandler.removeMessages(MSG_UPDATE_TIMES);
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
|
||||
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
|
||||
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
|
||||
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
|
||||
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
mActiveItems.remove(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mCurDialog != null) {
|
||||
mCurDialog.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
736
src/com/android/settings/applications/RunningState.java
Normal file
736
src/com/android/settings/applications/RunningState.java
Normal file
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.settings.R;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageItemInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Debug;
|
||||
import android.os.RemoteException;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Singleton for retrieving and monitoring the state about all running
|
||||
* applications/processes/services.
|
||||
*/
|
||||
public class RunningState {
|
||||
|
||||
final SparseArray<HashMap<String, ProcessItem>> mProcesses
|
||||
= new SparseArray<HashMap<String, ProcessItem>>();
|
||||
final SparseArray<ProcessItem> mActiveProcesses
|
||||
= new SparseArray<ProcessItem>();
|
||||
final ServiceProcessComparator mServiceProcessComparator
|
||||
= new ServiceProcessComparator();
|
||||
|
||||
// Temporary for finding process dependencies.
|
||||
final SparseArray<ProcessItem> mRunningProcesses
|
||||
= new SparseArray<ProcessItem>();
|
||||
|
||||
final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
|
||||
final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
|
||||
|
||||
int mSequence = 0;
|
||||
|
||||
// ----- following protected by mLock -----
|
||||
|
||||
// Lock for protecting the state that will be shared between the
|
||||
// background update thread and the UI thread.
|
||||
final Object mLock = new Object();
|
||||
|
||||
ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
|
||||
ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
|
||||
|
||||
int mNumBackgroundProcesses;
|
||||
long mBackgroundProcessMemory;
|
||||
int mNumForegroundProcesses;
|
||||
long mForegroundProcessMemory;
|
||||
int mNumServiceProcesses;
|
||||
long mServiceProcessMemory;
|
||||
|
||||
static class BaseItem {
|
||||
final boolean mIsProcess;
|
||||
|
||||
PackageItemInfo mPackageInfo;
|
||||
CharSequence mDisplayLabel;
|
||||
String mLabel;
|
||||
String mDescription;
|
||||
|
||||
int mCurSeq;
|
||||
|
||||
long mActiveSince;
|
||||
long mSize;
|
||||
String mSizeStr;
|
||||
String mCurSizeStr;
|
||||
boolean mNeedDivider;
|
||||
|
||||
public BaseItem(boolean isProcess) {
|
||||
mIsProcess = isProcess;
|
||||
}
|
||||
}
|
||||
|
||||
static class ServiceItem extends BaseItem {
|
||||
ActivityManager.RunningServiceInfo mRunningService;
|
||||
ServiceInfo mServiceInfo;
|
||||
boolean mShownAsStarted;
|
||||
|
||||
MergedItem mMergedItem;
|
||||
|
||||
public ServiceItem() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
static class ProcessItem extends BaseItem {
|
||||
final HashMap<ComponentName, ServiceItem> mServices
|
||||
= new HashMap<ComponentName, ServiceItem>();
|
||||
final SparseArray<ProcessItem> mDependentProcesses
|
||||
= new SparseArray<ProcessItem>();
|
||||
|
||||
final int mUid;
|
||||
final String mProcessName;
|
||||
int mPid;
|
||||
|
||||
ProcessItem mClient;
|
||||
int mLastNumDependentProcesses;
|
||||
|
||||
int mRunningSeq;
|
||||
ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
|
||||
|
||||
// Purely for sorting.
|
||||
boolean mIsSystem;
|
||||
boolean mIsStarted;
|
||||
long mActiveSince;
|
||||
|
||||
public ProcessItem(Context context, int uid, String processName) {
|
||||
super(true);
|
||||
mDescription = context.getResources().getString(
|
||||
R.string.service_process_name, processName);
|
||||
mUid = uid;
|
||||
mProcessName = processName;
|
||||
}
|
||||
|
||||
void ensureLabel(PackageManager pm) {
|
||||
if (mLabel != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
|
||||
if (ai.uid == mUid) {
|
||||
mDisplayLabel = ai.loadLabel(pm);
|
||||
mLabel = mDisplayLabel.toString();
|
||||
mPackageInfo = ai;
|
||||
return;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
|
||||
// If we couldn't get information about the overall
|
||||
// process, try to find something about the uid.
|
||||
String[] pkgs = pm.getPackagesForUid(mUid);
|
||||
|
||||
// If there is one package with this uid, that is what we want.
|
||||
if (pkgs.length == 1) {
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
|
||||
mDisplayLabel = ai.loadLabel(pm);
|
||||
mLabel = mDisplayLabel.toString();
|
||||
mPackageInfo = ai;
|
||||
return;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// If there are multiple, see if one gives us the official name
|
||||
// for this uid.
|
||||
for (String name : pkgs) {
|
||||
try {
|
||||
PackageInfo pi = pm.getPackageInfo(name, 0);
|
||||
if (pi.sharedUserLabel != 0) {
|
||||
CharSequence nm = pm.getText(name,
|
||||
pi.sharedUserLabel, pi.applicationInfo);
|
||||
if (nm != null) {
|
||||
mDisplayLabel = nm;
|
||||
mLabel = nm.toString();
|
||||
mPackageInfo = pi.applicationInfo;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// If still don't have anything to display, just use the
|
||||
// service info.
|
||||
if (mServices.size() > 0) {
|
||||
mPackageInfo = mServices.values().iterator().next()
|
||||
.mServiceInfo.applicationInfo;
|
||||
mDisplayLabel = mPackageInfo.loadLabel(pm);
|
||||
mLabel = mDisplayLabel.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally... whatever, just pick the first package's name.
|
||||
try {
|
||||
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
|
||||
mDisplayLabel = ai.loadLabel(pm);
|
||||
mLabel = mDisplayLabel.toString();
|
||||
mPackageInfo = ai;
|
||||
return;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
boolean updateService(Context context,
|
||||
ActivityManager.RunningServiceInfo service) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
|
||||
boolean changed = false;
|
||||
ServiceItem si = mServices.get(service.service);
|
||||
if (si == null) {
|
||||
changed = true;
|
||||
si = new ServiceItem();
|
||||
si.mRunningService = service;
|
||||
try {
|
||||
si.mServiceInfo = pm.getServiceInfo(service.service, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
si.mDisplayLabel = makeLabel(pm,
|
||||
si.mRunningService.service.getClassName(), si.mServiceInfo);
|
||||
mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
|
||||
si.mPackageInfo = si.mServiceInfo.applicationInfo;
|
||||
mServices.put(service.service, si);
|
||||
}
|
||||
si.mCurSeq = mCurSeq;
|
||||
si.mRunningService = service;
|
||||
long activeSince = service.restarting == 0 ? service.activeSince : -1;
|
||||
if (si.mActiveSince != activeSince) {
|
||||
si.mActiveSince = activeSince;
|
||||
changed = true;
|
||||
}
|
||||
if (service.clientPackage != null && service.clientLabel != 0) {
|
||||
if (si.mShownAsStarted) {
|
||||
si.mShownAsStarted = false;
|
||||
changed = true;
|
||||
}
|
||||
try {
|
||||
Resources clientr = pm.getResourcesForApplication(service.clientPackage);
|
||||
String label = clientr.getString(service.clientLabel);
|
||||
si.mDescription = context.getResources().getString(
|
||||
R.string.service_client_name, label);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
si.mDescription = null;
|
||||
}
|
||||
} else {
|
||||
if (!si.mShownAsStarted) {
|
||||
si.mShownAsStarted = true;
|
||||
changed = true;
|
||||
}
|
||||
si.mDescription = context.getResources().getString(
|
||||
R.string.service_started_by_app);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
|
||||
mSize = ((long)mem.getTotalPss()) * 1024;
|
||||
if (mCurSeq == curSeq) {
|
||||
String sizeStr = Formatter.formatShortFileSize(
|
||||
context, mSize);
|
||||
if (!sizeStr.equals(mSizeStr)){
|
||||
mSizeStr = sizeStr;
|
||||
// We update this on the second tick where we update just
|
||||
// the text in the current items, so no need to say we
|
||||
// changed here.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
|
||||
final int NP = mDependentProcesses.size();
|
||||
boolean changed = false;
|
||||
for (int i=0; i<NP; i++) {
|
||||
ProcessItem proc = mDependentProcesses.valueAt(i);
|
||||
if (proc.mClient != this) {
|
||||
changed = true;
|
||||
proc.mClient = this;
|
||||
}
|
||||
proc.mCurSeq = curSeq;
|
||||
proc.ensureLabel(pm);
|
||||
changed |= proc.buildDependencyChain(context, pm, curSeq);
|
||||
}
|
||||
|
||||
if (mLastNumDependentProcesses != mDependentProcesses.size()) {
|
||||
changed = true;
|
||||
mLastNumDependentProcesses = mDependentProcesses.size();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void addDependentProcesses(ArrayList<BaseItem> dest,
|
||||
ArrayList<ProcessItem> destProc) {
|
||||
final int NP = mDependentProcesses.size();
|
||||
for (int i=0; i<NP; i++) {
|
||||
ProcessItem proc = mDependentProcesses.valueAt(i);
|
||||
proc.addDependentProcesses(dest, destProc);
|
||||
dest.add(proc);
|
||||
if (proc.mPid > 0) {
|
||||
destProc.add(proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class MergedItem extends BaseItem {
|
||||
ProcessItem mProcess;
|
||||
final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
|
||||
final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
|
||||
|
||||
MergedItem() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
boolean update(Context context) {
|
||||
mPackageInfo = mProcess.mPackageInfo;
|
||||
mDisplayLabel = mProcess.mDisplayLabel;
|
||||
mLabel = mProcess.mLabel;
|
||||
|
||||
int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
|
||||
int numServices = mServices.size();
|
||||
int resid = R.string.running_processes_item_description_s_s;
|
||||
if (numProcesses != 1) {
|
||||
resid = numServices != 1
|
||||
? R.string.running_processes_item_description_p_p
|
||||
: R.string.running_processes_item_description_p_s;
|
||||
} else if (numServices != 1) {
|
||||
resid = R.string.running_processes_item_description_s_p;
|
||||
}
|
||||
mDescription = context.getResources().getString(resid, numProcesses, numServices);
|
||||
|
||||
mActiveSince = -1;
|
||||
for (int i=0; i<mServices.size(); i++) {
|
||||
ServiceItem si = mServices.get(i);
|
||||
if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
|
||||
mActiveSince = si.mActiveSince;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean updateSize(Context context) {
|
||||
mSize = mProcess.mSize;
|
||||
for (int i=0; i<mOtherProcesses.size(); i++) {
|
||||
mSize += mOtherProcesses.get(i).mSize;
|
||||
}
|
||||
|
||||
String sizeStr = Formatter.formatShortFileSize(
|
||||
context, mSize);
|
||||
if (!sizeStr.equals(mSizeStr)){
|
||||
mSizeStr = sizeStr;
|
||||
// We update this on the second tick where we update just
|
||||
// the text in the current items, so no need to say we
|
||||
// changed here.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class ServiceProcessComparator implements Comparator<ProcessItem> {
|
||||
public int compare(ProcessItem object1, ProcessItem object2) {
|
||||
if (object1.mIsStarted != object2.mIsStarted) {
|
||||
// Non-started processes go last.
|
||||
return object1.mIsStarted ? -1 : 1;
|
||||
}
|
||||
if (object1.mIsSystem != object2.mIsSystem) {
|
||||
// System processes go below non-system.
|
||||
return object1.mIsSystem ? 1 : -1;
|
||||
}
|
||||
if (object1.mActiveSince != object2.mActiveSince) {
|
||||
// Remaining ones are sorted with the longest running
|
||||
// services last.
|
||||
return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static CharSequence makeLabel(PackageManager pm,
|
||||
String className, PackageItemInfo item) {
|
||||
if (item != null && (item.labelRes != 0
|
||||
|| item.nonLocalizedLabel != null)) {
|
||||
CharSequence label = item.loadLabel(pm);
|
||||
if (label != null) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
String label = className;
|
||||
int tail = label.lastIndexOf('.');
|
||||
if (tail >= 0) {
|
||||
label = label.substring(tail+1, label.length());
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
boolean update(Context context, ActivityManager am) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
|
||||
mSequence++;
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
List<ActivityManager.RunningServiceInfo> services
|
||||
= am.getRunningServices(RunningServices.MAX_SERVICES);
|
||||
final int NS = services != null ? services.size() : 0;
|
||||
for (int i=0; i<NS; i++) {
|
||||
ActivityManager.RunningServiceInfo si = services.get(i);
|
||||
// We are not interested in services that have not been started
|
||||
// and don't have a known client, because
|
||||
// there is nothing the user can do about them.
|
||||
if (!si.started && si.clientLabel == 0) {
|
||||
continue;
|
||||
}
|
||||
// We likewise don't care about services running in a
|
||||
// persistent process like the system or phone.
|
||||
if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
|
||||
!= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
|
||||
if (procs == null) {
|
||||
procs = new HashMap<String, ProcessItem>();
|
||||
mProcesses.put(si.uid, procs);
|
||||
}
|
||||
ProcessItem proc = procs.get(si.process);
|
||||
if (proc == null) {
|
||||
changed = true;
|
||||
proc = new ProcessItem(context, si.uid, si.process);
|
||||
procs.put(si.process, proc);
|
||||
}
|
||||
|
||||
if (proc.mCurSeq != mSequence) {
|
||||
int pid = si.restarting == 0 ? si.pid : 0;
|
||||
if (pid != proc.mPid) {
|
||||
changed = true;
|
||||
if (proc.mPid != pid) {
|
||||
if (proc.mPid != 0) {
|
||||
mActiveProcesses.remove(proc.mPid);
|
||||
}
|
||||
if (pid != 0) {
|
||||
mActiveProcesses.put(pid, proc);
|
||||
}
|
||||
proc.mPid = pid;
|
||||
}
|
||||
}
|
||||
proc.mDependentProcesses.clear();
|
||||
proc.mCurSeq = mSequence;
|
||||
}
|
||||
changed |= proc.updateService(context, si);
|
||||
}
|
||||
|
||||
// Now update the map of other processes that are running (but
|
||||
// don't have services actively running inside them).
|
||||
List<ActivityManager.RunningAppProcessInfo> processes
|
||||
= am.getRunningAppProcesses();
|
||||
final int NP = processes != null ? processes.size() : 0;
|
||||
for (int i=0; i<NP; i++) {
|
||||
ActivityManager.RunningAppProcessInfo pi = processes.get(i);
|
||||
ProcessItem proc = mActiveProcesses.get(pi.pid);
|
||||
if (proc == null) {
|
||||
// This process is not one that is a direct container
|
||||
// of a service, so look for it in the secondary
|
||||
// running list.
|
||||
proc = mRunningProcesses.get(pi.pid);
|
||||
if (proc == null) {
|
||||
proc = new ProcessItem(context, pi.uid, pi.processName);
|
||||
proc.mPid = pi.pid;
|
||||
mRunningProcesses.put(pi.pid, proc);
|
||||
}
|
||||
proc.mDependentProcesses.clear();
|
||||
}
|
||||
proc.mRunningSeq = mSequence;
|
||||
proc.mRunningProcessInfo = pi;
|
||||
}
|
||||
|
||||
// Build the chains from client processes to the process they are
|
||||
// dependent on; also remove any old running processes.
|
||||
int NRP = mRunningProcesses.size();
|
||||
for (int i=0; i<NRP; i++) {
|
||||
ProcessItem proc = mRunningProcesses.valueAt(i);
|
||||
if (proc.mRunningSeq == mSequence) {
|
||||
int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
|
||||
if (clientPid != 0) {
|
||||
ProcessItem client = mActiveProcesses.get(clientPid);
|
||||
if (client == null) {
|
||||
client = mRunningProcesses.get(clientPid);
|
||||
}
|
||||
if (client != null) {
|
||||
client.mDependentProcesses.put(proc.mPid, proc);
|
||||
}
|
||||
} else {
|
||||
// In this pass the process doesn't have a client.
|
||||
// Clear to make sure if it later gets the same one
|
||||
// that we will detect the change.
|
||||
proc.mClient = null;
|
||||
}
|
||||
} else {
|
||||
mRunningProcesses.remove(mRunningProcesses.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Follow the tree from all primary service processes to all
|
||||
// processes they are dependent on, marking these processes as
|
||||
// still being active and determining if anything has changed.
|
||||
final int NAP = mActiveProcesses.size();
|
||||
for (int i=0; i<NAP; i++) {
|
||||
ProcessItem proc = mActiveProcesses.valueAt(i);
|
||||
if (proc.mCurSeq == mSequence) {
|
||||
changed |= proc.buildDependencyChain(context, pm, mSequence);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for services and their primary processes that no longer exist...
|
||||
for (int i=0; i<mProcesses.size(); i++) {
|
||||
HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
|
||||
Iterator<ProcessItem> pit = procs.values().iterator();
|
||||
while (pit.hasNext()) {
|
||||
ProcessItem pi = pit.next();
|
||||
if (pi.mCurSeq == mSequence) {
|
||||
pi.ensureLabel(pm);
|
||||
if (pi.mPid == 0) {
|
||||
// Sanity: a non-process can't be dependent on
|
||||
// anything.
|
||||
pi.mDependentProcesses.clear();
|
||||
}
|
||||
} else {
|
||||
changed = true;
|
||||
pit.remove();
|
||||
if (procs.size() == 0) {
|
||||
mProcesses.remove(mProcesses.keyAt(i));
|
||||
}
|
||||
if (pi.mPid != 0) {
|
||||
mActiveProcesses.remove(pi.mPid);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Iterator<ServiceItem> sit = pi.mServices.values().iterator();
|
||||
while (sit.hasNext()) {
|
||||
ServiceItem si = sit.next();
|
||||
if (si.mCurSeq != mSequence) {
|
||||
changed = true;
|
||||
sit.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
// First determine an order for the services.
|
||||
ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
|
||||
for (int i=0; i<mProcesses.size(); i++) {
|
||||
for (ProcessItem pi : mProcesses.valueAt(i).values()) {
|
||||
pi.mIsSystem = false;
|
||||
pi.mIsStarted = true;
|
||||
pi.mActiveSince = Long.MAX_VALUE;
|
||||
for (ServiceItem si : pi.mServices.values()) {
|
||||
if (si.mServiceInfo != null
|
||||
&& (si.mServiceInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
pi.mIsSystem = true;
|
||||
}
|
||||
if (si.mRunningService != null
|
||||
&& si.mRunningService.clientLabel != 0) {
|
||||
pi.mIsStarted = false;
|
||||
if (pi.mActiveSince > si.mRunningService.activeSince) {
|
||||
pi.mActiveSince = si.mRunningService.activeSince;
|
||||
}
|
||||
}
|
||||
}
|
||||
sortedProcesses.add(pi);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(sortedProcesses, mServiceProcessComparator);
|
||||
|
||||
ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
|
||||
ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
|
||||
mProcessItems.clear();
|
||||
for (int i=0; i<sortedProcesses.size(); i++) {
|
||||
ProcessItem pi = sortedProcesses.get(i);
|
||||
pi.mNeedDivider = false;
|
||||
|
||||
int firstProc = mProcessItems.size();
|
||||
// First add processes we are dependent on.
|
||||
pi.addDependentProcesses(newItems, mProcessItems);
|
||||
// And add the process itself.
|
||||
newItems.add(pi);
|
||||
if (pi.mPid > 0) {
|
||||
mProcessItems.add(pi);
|
||||
}
|
||||
|
||||
// Now add the services running in it.
|
||||
MergedItem mergedItem = null;
|
||||
boolean haveAllMerged = false;
|
||||
boolean needDivider = false;
|
||||
for (ServiceItem si : pi.mServices.values()) {
|
||||
si.mNeedDivider = needDivider;
|
||||
needDivider = true;
|
||||
newItems.add(si);
|
||||
if (si.mMergedItem != null) {
|
||||
if (mergedItem != null && mergedItem != si.mMergedItem) {
|
||||
haveAllMerged = false;
|
||||
}
|
||||
mergedItem = si.mMergedItem;
|
||||
} else {
|
||||
haveAllMerged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveAllMerged || mergedItem == null
|
||||
|| mergedItem.mServices.size() != pi.mServices.size()) {
|
||||
// Whoops, we need to build a new MergedItem!
|
||||
mergedItem = new MergedItem();
|
||||
for (ServiceItem si : pi.mServices.values()) {
|
||||
mergedItem.mServices.add(si);
|
||||
si.mMergedItem = mergedItem;
|
||||
}
|
||||
mergedItem.mProcess = pi;
|
||||
mergedItem.mOtherProcesses.clear();
|
||||
for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
|
||||
mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
|
||||
}
|
||||
}
|
||||
|
||||
mergedItem.update(context);
|
||||
newMergedItems.add(mergedItem);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mItems = newItems;
|
||||
mMergedItems = newMergedItems;
|
||||
}
|
||||
}
|
||||
|
||||
// Count number of interesting other (non-active) processes, and
|
||||
// build a list of all processes we will retrieve memory for.
|
||||
mAllProcessItems.clear();
|
||||
mAllProcessItems.addAll(mProcessItems);
|
||||
int numBackgroundProcesses = 0;
|
||||
int numForegroundProcesses = 0;
|
||||
int numServiceProcesses = 0;
|
||||
NRP = mRunningProcesses.size();
|
||||
for (int i=0; i<NRP; i++) {
|
||||
ProcessItem proc = mRunningProcesses.valueAt(i);
|
||||
if (proc.mCurSeq != mSequence) {
|
||||
// We didn't hit this process as a dependency on one
|
||||
// of our active ones, so add it up if needed.
|
||||
if (proc.mRunningProcessInfo.importance >=
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
|
||||
numBackgroundProcesses++;
|
||||
mAllProcessItems.add(proc);
|
||||
} else if (proc.mRunningProcessInfo.importance <=
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
|
||||
numForegroundProcesses++;
|
||||
mAllProcessItems.add(proc);
|
||||
} else {
|
||||
Log.i(RunningServices.TAG, "Unknown non-service process: "
|
||||
+ proc.mProcessName + " #" + proc.mPid);
|
||||
}
|
||||
} else {
|
||||
numServiceProcesses++;
|
||||
}
|
||||
}
|
||||
|
||||
long backgroundProcessMemory = 0;
|
||||
long foregroundProcessMemory = 0;
|
||||
long serviceProcessMemory = 0;
|
||||
try {
|
||||
final int numProc = mAllProcessItems.size();
|
||||
int[] pids = new int[numProc];
|
||||
for (int i=0; i<numProc; i++) {
|
||||
pids[i] = mAllProcessItems.get(i).mPid;
|
||||
}
|
||||
Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
|
||||
.getProcessMemoryInfo(pids);
|
||||
for (int i=pids.length-1; i>=0; i--) {
|
||||
ProcessItem proc = mAllProcessItems.get(i);
|
||||
changed |= proc.updateSize(context, mem[i], mSequence);
|
||||
if (proc.mCurSeq == mSequence) {
|
||||
serviceProcessMemory += proc.mSize;
|
||||
} else if (proc.mRunningProcessInfo.importance >=
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
|
||||
backgroundProcessMemory += proc.mSize;
|
||||
} else if (proc.mRunningProcessInfo.importance <=
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
|
||||
foregroundProcessMemory += proc.mSize;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
for (int i=0; i<mMergedItems.size(); i++) {
|
||||
mMergedItems.get(i).updateSize(context);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
mNumBackgroundProcesses = numBackgroundProcesses;
|
||||
mNumForegroundProcesses = numForegroundProcesses;
|
||||
mNumServiceProcesses = numServiceProcesses;
|
||||
mBackgroundProcessMemory = backgroundProcessMemory;
|
||||
mForegroundProcessMemory = foregroundProcessMemory;
|
||||
mServiceProcessMemory = serviceProcessMemory;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
ArrayList<BaseItem> getCurrentItems() {
|
||||
synchronized (mLock) {
|
||||
return mItems;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<MergedItem> getCurrentMergedItems() {
|
||||
synchronized (mLock) {
|
||||
return mMergedItems;
|
||||
}
|
||||
}
|
||||
}
|
@@ -40,9 +40,9 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.android.settings.InstalledAppDetails;
|
||||
import com.android.settings.ManageApplications;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.InstalledAppDetails;
|
||||
import com.android.settings.applications.ManageApplications;
|
||||
|
||||
public class PowerUsageDetail extends Activity implements Button.OnClickListener {
|
||||
|
||||
@@ -248,9 +248,9 @@ public class PowerUsageDetail extends Activity implements Button.OnClickListener
|
||||
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
|
||||
break;
|
||||
case ACTION_APP_DETAILS:
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.fromParts("package", mPackages[0], null));
|
||||
intent.setClass(this, InstalledAppDetails.class);
|
||||
intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]);
|
||||
startActivity(intent);
|
||||
break;
|
||||
case ACTION_SECURITY_SETTINGS:
|
||||
|
Reference in New Issue
Block a user