- for fragments that do not implement the preference screen, change them to inherit from InstrumentedFragment instead. Change-Id: I791c2634024bd2c248efea955be5c680180d735c Fixes: 68277111 Test: make RunSettingsRoboTests
657 lines
26 KiB
Java
657 lines
26 KiB
Java
package com.android.settings.applications;
|
|
|
|
import android.app.Activity;
|
|
import android.app.ActivityManager;
|
|
import android.app.AlertDialog;
|
|
import android.app.ApplicationErrorReport;
|
|
import android.app.Dialog;
|
|
import android.app.DialogFragment;
|
|
import android.app.PendingIntent;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.content.IntentSender;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
|
import android.content.pm.ProviderInfo;
|
|
import android.content.pm.ServiceInfo;
|
|
import android.content.res.Resources;
|
|
import android.os.Bundle;
|
|
import android.os.Debug;
|
|
import android.os.SystemClock;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.Button;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
import com.android.settings.R;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.core.InstrumentedFragment;
|
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
|
import com.android.settingslib.utils.ThreadUtils;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
|
|
public class RunningServiceDetails extends InstrumentedFragment
|
|
implements RunningState.OnRefreshUiListener {
|
|
static final String TAG = "RunningServicesDetails";
|
|
|
|
static final String KEY_UID = "uid";
|
|
static final String KEY_USER_ID = "user_id";
|
|
static final String KEY_PROCESS = "process";
|
|
static final String KEY_BACKGROUND = "background";
|
|
|
|
static final int DIALOG_CONFIRM_STOP = 1;
|
|
|
|
ActivityManager mAm;
|
|
LayoutInflater mInflater;
|
|
|
|
RunningState mState;
|
|
boolean mHaveData;
|
|
|
|
int mUid;
|
|
int mUserId;
|
|
String mProcessName;
|
|
boolean mShowBackground;
|
|
|
|
RunningState.MergedItem mMergedItem;
|
|
|
|
View mRootView;
|
|
ViewGroup mAllDetails;
|
|
ViewGroup mSnippet;
|
|
RunningProcessesView.ActiveItem mSnippetActiveItem;
|
|
RunningProcessesView.ViewHolder mSnippetViewHolder;
|
|
|
|
int mNumServices, mNumProcesses;
|
|
|
|
TextView mServicesHeader;
|
|
TextView mProcessesHeader;
|
|
final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
|
|
|
|
class ActiveDetail implements View.OnClickListener {
|
|
View mRootView;
|
|
Button mStopButton;
|
|
Button mReportButton;
|
|
RunningState.ServiceItem mServiceItem;
|
|
RunningProcessesView.ActiveItem mActiveItem;
|
|
RunningProcessesView.ViewHolder mViewHolder;
|
|
PendingIntent mManageIntent;
|
|
ComponentName mInstaller;
|
|
|
|
void stopActiveService(boolean confirmed) {
|
|
RunningState.ServiceItem si = mServiceItem;
|
|
if (!confirmed) {
|
|
if ((si.mServiceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
|
|
showConfirmStopDialog(si.mRunningService.service);
|
|
return;
|
|
}
|
|
}
|
|
getActivity().stopService(new Intent().setComponent(si.mRunningService.service));
|
|
if (mMergedItem == null) {
|
|
// If this is gone, we are gone.
|
|
mState.updateNow();
|
|
finish();
|
|
} else if (!mShowBackground && mMergedItem.mServices.size() <= 1) {
|
|
// If there was only one service, we are finishing it,
|
|
// so no reason for the UI to stick around.
|
|
mState.updateNow();
|
|
finish();
|
|
} else {
|
|
mState.updateNow();
|
|
}
|
|
}
|
|
|
|
public void onClick(View v) {
|
|
if (v == mReportButton) {
|
|
ApplicationErrorReport report = new ApplicationErrorReport();
|
|
report.type = ApplicationErrorReport.TYPE_RUNNING_SERVICE;
|
|
report.packageName = mServiceItem.mServiceInfo.packageName;
|
|
report.installerPackageName = mInstaller.getPackageName();
|
|
report.processName = mServiceItem.mRunningService.process;
|
|
report.time = System.currentTimeMillis();
|
|
report.systemApp = (mServiceItem.mServiceInfo.applicationInfo.flags
|
|
& ApplicationInfo.FLAG_SYSTEM) != 0;
|
|
ApplicationErrorReport.RunningServiceInfo info
|
|
= new ApplicationErrorReport.RunningServiceInfo();
|
|
if (mActiveItem.mFirstRunTime >= 0) {
|
|
info.durationMillis = SystemClock.elapsedRealtime()-mActiveItem.mFirstRunTime;
|
|
} else {
|
|
info.durationMillis = -1;
|
|
}
|
|
ComponentName comp = new ComponentName(mServiceItem.mServiceInfo.packageName,
|
|
mServiceItem.mServiceInfo.name);
|
|
File filename = getActivity().getFileStreamPath("service_dump.txt");
|
|
FileOutputStream output = null;
|
|
try {
|
|
output = new FileOutputStream(filename);
|
|
Debug.dumpService("activity", output.getFD(),
|
|
new String[] { "-a", "service", comp.flattenToString() });
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Can't dump service: " + comp, e);
|
|
} finally {
|
|
if (output != null) try { output.close(); } catch (IOException e) {}
|
|
}
|
|
FileInputStream input = null;
|
|
try {
|
|
input = new FileInputStream(filename);
|
|
byte[] buffer = new byte[(int) filename.length()];
|
|
input.read(buffer);
|
|
info.serviceDetails = new String(buffer);
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Can't read service dump: " + comp, e);
|
|
} finally {
|
|
if (input != null) try { input.close(); } catch (IOException e) {}
|
|
}
|
|
filename.delete();
|
|
Log.i(TAG, "Details: " + info.serviceDetails);
|
|
report.runningServiceInfo = info;
|
|
Intent result = new Intent(Intent.ACTION_APP_ERROR);
|
|
result.setComponent(mInstaller);
|
|
result.putExtra(Intent.EXTRA_BUG_REPORT, report);
|
|
result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
startActivity(result);
|
|
return;
|
|
}
|
|
|
|
if (mManageIntent != null) {
|
|
try {
|
|
getActivity().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 if (mServiceItem != null) {
|
|
stopActiveService(false);
|
|
} else if (mActiveItem.mItem.mBackground) {
|
|
// Background process. Just kill it.
|
|
mAm.killBackgroundProcesses(mActiveItem.mItem.mPackageInfo.packageName);
|
|
finish();
|
|
} else {
|
|
// Heavy-weight process. We'll do a force-stop on it.
|
|
mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
|
|
finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
StringBuilder mBuilder = new StringBuilder(128);
|
|
|
|
boolean findMergedItem() {
|
|
RunningState.MergedItem item = null;
|
|
ArrayList<RunningState.MergedItem> newItems = mShowBackground
|
|
? mState.getCurrentBackgroundItems() : mState.getCurrentMergedItems();
|
|
if (newItems != null) {
|
|
for (int i=0; i<newItems.size(); i++) {
|
|
RunningState.MergedItem mi = newItems.get(i);
|
|
if (mi.mUserId != mUserId) {
|
|
continue;
|
|
}
|
|
if (mUid >= 0 && mi.mProcess != null && mi.mProcess.mUid != mUid) {
|
|
continue;
|
|
}
|
|
if (mProcessName == null || (mi.mProcess != null
|
|
&& mProcessName.equals(mi.mProcess.mProcessName))) {
|
|
item = mi;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mMergedItem != item) {
|
|
mMergedItem = item;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void addServicesHeader() {
|
|
if (mNumServices == 0) {
|
|
mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
|
|
mAllDetails, false);
|
|
mServicesHeader.setText(R.string.runningservicedetails_services_title);
|
|
mAllDetails.addView(mServicesHeader);
|
|
}
|
|
mNumServices++;
|
|
}
|
|
|
|
void addProcessesHeader() {
|
|
if (mNumProcesses == 0) {
|
|
mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
|
|
mAllDetails, false);
|
|
mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
|
|
mAllDetails.addView(mProcessesHeader);
|
|
}
|
|
mNumProcesses++;
|
|
}
|
|
|
|
void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi,
|
|
boolean isService, boolean inclDetails) {
|
|
if (isService) {
|
|
addServicesHeader();
|
|
} else if (mi.mUserId != UserHandle.myUserId()) {
|
|
// This is being called for another user, and is not a service...
|
|
// That is, it is a background processes, being added for the
|
|
// details of a user. In this case we want a header for processes,
|
|
// since the top subject line is for the user.
|
|
addProcessesHeader();
|
|
}
|
|
|
|
RunningState.BaseItem bi = si != null ? si : mi;
|
|
|
|
ActiveDetail detail = new ActiveDetail();
|
|
View root = mInflater.inflate(R.layout.running_service_details_service,
|
|
mAllDetails, false);
|
|
mAllDetails.addView(root);
|
|
detail.mRootView = root;
|
|
detail.mServiceItem = si;
|
|
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
|
|
detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
|
|
|
|
if (!inclDetails) {
|
|
root.findViewById(R.id.service).setVisibility(View.GONE);
|
|
}
|
|
|
|
if (si != null && si.mRunningService.clientLabel != 0) {
|
|
detail.mManageIntent = mAm.getRunningServiceControlPanel(
|
|
si.mRunningService.service);
|
|
}
|
|
|
|
TextView description = (TextView)root.findViewById(R.id.comp_description);
|
|
detail.mStopButton = (Button)root.findViewById(R.id.left_button);
|
|
detail.mReportButton = (Button)root.findViewById(R.id.right_button);
|
|
|
|
if (isService && mi.mUserId != UserHandle.myUserId()) {
|
|
// For services from other users, we don't show any description or
|
|
// controls, because the current user can not perform
|
|
// actions on them.
|
|
description.setVisibility(View.GONE);
|
|
root.findViewById(R.id.control_buttons_panel).setVisibility(View.GONE);
|
|
} else {
|
|
if (si != null && si.mServiceInfo.descriptionRes != 0) {
|
|
description.setText(getActivity().getPackageManager().getText(
|
|
si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
|
|
si.mServiceInfo.applicationInfo));
|
|
} else {
|
|
if (mi.mBackground) {
|
|
description.setText(R.string.background_process_stop_description);
|
|
} else if (detail.mManageIntent != null) {
|
|
try {
|
|
Resources clientr = getActivity().getPackageManager().getResourcesForApplication(
|
|
si.mRunningService.clientPackage);
|
|
String label = clientr.getString(si.mRunningService.clientLabel);
|
|
description.setText(getActivity().getString(R.string.service_manage_description,
|
|
label));
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
}
|
|
} else {
|
|
description.setText(getActivity().getText(si != null
|
|
? R.string.service_stop_description
|
|
: R.string.heavy_weight_stop_description));
|
|
}
|
|
}
|
|
|
|
detail.mStopButton.setOnClickListener(detail);
|
|
detail.mStopButton.setText(getActivity().getText(detail.mManageIntent != null
|
|
? R.string.service_manage : R.string.service_stop));
|
|
detail.mReportButton.setOnClickListener(detail);
|
|
detail.mReportButton.setText(com.android.internal.R.string.report);
|
|
// check if error reporting is enabled in secure settings
|
|
int enabled = Settings.Global.getInt(getActivity().getContentResolver(),
|
|
Settings.Global.SEND_ACTION_APP_ERROR, 0);
|
|
if (enabled != 0 && si != null) {
|
|
detail.mInstaller = ApplicationErrorReport.getErrorReportReceiver(
|
|
getActivity(), si.mServiceInfo.packageName,
|
|
si.mServiceInfo.applicationInfo.flags);
|
|
detail.mReportButton.setEnabled(detail.mInstaller != null);
|
|
} else {
|
|
detail.mReportButton.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
mActiveDetails.add(detail);
|
|
}
|
|
|
|
void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
|
|
addProcessesHeader();
|
|
|
|
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 (pi.mUserId != UserHandle.myUserId()) {
|
|
// Processes for another user are all shown batched together; there is
|
|
// no reason to have a description.
|
|
description.setVisibility(View.GONE);
|
|
} else if (isMain) {
|
|
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;
|
|
if (rpi.importanceReasonComponent != null) {
|
|
try {
|
|
ProviderInfo prov = getActivity().getPackageManager().getProviderInfo(
|
|
rpi.importanceReasonComponent, 0);
|
|
label = RunningState.makeLabel(getActivity().getPackageManager(),
|
|
prov.name, prov);
|
|
} catch (NameNotFoundException e) {
|
|
}
|
|
}
|
|
break;
|
|
case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
|
|
textid = R.string.process_service_in_use_description;
|
|
if (rpi.importanceReasonComponent != null) {
|
|
try {
|
|
ServiceInfo serv = getActivity().getPackageManager().getServiceInfo(
|
|
rpi.importanceReasonComponent, 0);
|
|
label = RunningState.makeLabel(getActivity().getPackageManager(),
|
|
serv.name, serv);
|
|
} catch (NameNotFoundException e) {
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (textid != 0 && label != null) {
|
|
description.setText(getActivity().getString(textid, label));
|
|
}
|
|
}
|
|
|
|
mActiveDetails.add(detail);
|
|
}
|
|
|
|
void addDetailsViews(RunningState.MergedItem item, boolean inclServices,
|
|
boolean inclProcesses) {
|
|
if (item != null) {
|
|
if (inclServices) {
|
|
for (int i=0; i<item.mServices.size(); i++) {
|
|
addServiceDetailsView(item.mServices.get(i), item, true, true);
|
|
}
|
|
}
|
|
|
|
if (inclProcesses) {
|
|
if (item.mServices.size() <= 0) {
|
|
// This item does not have any services, so it must be
|
|
// another interesting process... we will put a fake service
|
|
// entry for it, to allow the user to "stop" it.
|
|
addServiceDetailsView(null, item, false, item.mUserId != UserHandle.myUserId());
|
|
} else {
|
|
// This screen is actually showing services, so also show
|
|
// the process details.
|
|
for (int i=-1; i<item.mOtherProcesses.size(); i++) {
|
|
RunningState.ProcessItem pi = i < 0 ? item.mProcess
|
|
: item.mOtherProcesses.get(i);
|
|
if (pi != null && pi.mPid <= 0) {
|
|
continue;
|
|
}
|
|
|
|
addProcessDetailsView(pi, i < 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
mNumServices = mNumProcesses = 0;
|
|
|
|
if (mMergedItem != null) {
|
|
if (mMergedItem.mUser != null) {
|
|
ArrayList<RunningState.MergedItem> items;
|
|
if (mShowBackground) {
|
|
items = new ArrayList<RunningState.MergedItem>(mMergedItem.mChildren);
|
|
Collections.sort(items, mState.mBackgroundComparator);
|
|
} else {
|
|
items = mMergedItem.mChildren;
|
|
}
|
|
for (int i=0; i<items.size(); i++) {
|
|
addDetailsViews(items.get(i), true, false);
|
|
}
|
|
for (int i=0; i<items.size(); i++) {
|
|
addDetailsViews(items.get(i), false, true);
|
|
}
|
|
} else {
|
|
addDetailsViews(mMergedItem, true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
private void finish() {
|
|
ThreadUtils.postOnMainThread(() -> {
|
|
final Activity a = getActivity();
|
|
if (a != null) {
|
|
a.onBackPressed();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setHasOptionsMenu(true);
|
|
mUid = getArguments().getInt(KEY_UID, -1);
|
|
mUserId = getArguments().getInt(KEY_USER_ID, 0);
|
|
mProcessName = getArguments().getString(KEY_PROCESS, null);
|
|
mShowBackground = getArguments().getBoolean(KEY_BACKGROUND, false);
|
|
|
|
mAm = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE);
|
|
mInflater = (LayoutInflater) getActivity().getSystemService(
|
|
Context.LAYOUT_INFLATER_SERVICE);
|
|
|
|
mState = RunningState.getInstance(getActivity());
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(
|
|
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
final View view = inflater.inflate(R.layout.running_service_details, container, false);
|
|
Utils.prepareCustomPreferencesList(container, view, view, false);
|
|
|
|
mRootView = view;
|
|
mAllDetails = (ViewGroup)view.findViewById(R.id.all_details);
|
|
mSnippet = (ViewGroup)view.findViewById(R.id.snippet);
|
|
mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
|
|
|
|
// We want to retrieve the data right now, so any active managed
|
|
// dialog that gets created can find it.
|
|
ensureData();
|
|
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
mHaveData = false;
|
|
mState.pause();
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.RUNNING_SERVICE_DETAILS;
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
ensureData();
|
|
}
|
|
|
|
ActiveDetail activeDetailForService(ComponentName comp) {
|
|
for (int i=0; i<mActiveDetails.size(); i++) {
|
|
ActiveDetail ad = mActiveDetails.get(i);
|
|
if (ad.mServiceItem != null && ad.mServiceItem.mRunningService != null
|
|
&& comp.equals(ad.mServiceItem.mRunningService.service)) {
|
|
return ad;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void showConfirmStopDialog(ComponentName comp) {
|
|
DialogFragment newFragment = MyAlertDialogFragment.newConfirmStop(
|
|
DIALOG_CONFIRM_STOP, comp);
|
|
newFragment.setTargetFragment(this, 0);
|
|
newFragment.show(getFragmentManager(), "confirmstop");
|
|
}
|
|
|
|
public static class MyAlertDialogFragment extends InstrumentedDialogFragment {
|
|
|
|
public static MyAlertDialogFragment newConfirmStop(int id, ComponentName comp) {
|
|
MyAlertDialogFragment frag = new MyAlertDialogFragment();
|
|
Bundle args = new Bundle();
|
|
args.putInt("id", id);
|
|
args.putParcelable("comp", comp);
|
|
frag.setArguments(args);
|
|
return frag;
|
|
}
|
|
|
|
RunningServiceDetails getOwner() {
|
|
return (RunningServiceDetails)getTargetFragment();
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
int id = getArguments().getInt("id");
|
|
switch (id) {
|
|
case DIALOG_CONFIRM_STOP: {
|
|
final ComponentName comp = (ComponentName)getArguments().getParcelable("comp");
|
|
if (getOwner().activeDetailForService(comp) == null) {
|
|
return null;
|
|
}
|
|
|
|
return new AlertDialog.Builder(getActivity())
|
|
.setTitle(getActivity().getString(R.string.runningservicedetails_stop_dlg_title))
|
|
.setMessage(getActivity().getString(R.string.runningservicedetails_stop_dlg_text))
|
|
.setPositiveButton(R.string.dlg_ok,
|
|
new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
ActiveDetail ad = getOwner().activeDetailForService(comp);
|
|
if (ad != null) {
|
|
ad.stopActiveService(true);
|
|
}
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.dlg_cancel, null)
|
|
.create();
|
|
}
|
|
}
|
|
throw new IllegalArgumentException("unknown id " + id);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_RUNNIGN_SERVICE;
|
|
}
|
|
}
|
|
|
|
void ensureData() {
|
|
if (!mHaveData) {
|
|
mHaveData = true;
|
|
mState.resume(this);
|
|
|
|
// We want to go away if the service being shown no longer exists,
|
|
// so we need to ensure we have done the initial data retrieval before
|
|
// showing our ui.
|
|
mState.waitForData();
|
|
|
|
// And since we know we have the data, let's show the UI right away
|
|
// to avoid flicker.
|
|
refreshUi(true);
|
|
}
|
|
}
|
|
|
|
void updateTimes() {
|
|
if (mSnippetActiveItem != null) {
|
|
mSnippetActiveItem.updateTime(getActivity(), mBuilder);
|
|
}
|
|
for (int i=0; i<mActiveDetails.size(); i++) {
|
|
mActiveDetails.get(i).mActiveItem.updateTime(getActivity(), mBuilder);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onRefreshUi(int what) {
|
|
if (getActivity() == null) return;
|
|
switch (what) {
|
|
case REFRESH_TIME:
|
|
updateTimes();
|
|
break;
|
|
case REFRESH_DATA:
|
|
refreshUi(false);
|
|
updateTimes();
|
|
break;
|
|
case REFRESH_STRUCTURE:
|
|
refreshUi(true);
|
|
updateTimes();
|
|
break;
|
|
}
|
|
}
|
|
}
|