Merge "Transparent system ui flags -> Translucent WM flags." into klp-dev
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index eeb6cba..4af0f51 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -169,7 +169,7 @@
} else if ("recolor".equals(name)) {
transition = new Recolor();
newTransition = true;
- } else if ("set".equals(name)) {
+ } else if ("transitionSet".equals(name)) {
transition = new TransitionSet();
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TransitionSet);
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 79cd8b6..4545e3b 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -30,6 +30,22 @@
* others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
* uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
* a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
+ *
+ * <p>A TransitionSet can be described in a resource file by using the
+ * tag <code>transitionSet</code>, along with the standard
+ * attributes of {@link android.R.styleable#TransitionSet} and
+ * {@link android.R.styleable#Transition}. Child transitions of the
+ * TransitionSet object can be loaded by adding those child tags inside the
+ * enclosing <code>transitionSet</code> tag. For example, the following xml
+ * describes a TransitionSet that plays a Fade and then a ChangeBounds
+ * transition on the affected view targets:</p>
+ * <pre>
+ * <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:ordering="sequential">
+ * <fade/>
+ * <changeBounds/>
+ * </transitionSet>
+ * </pre>
*/
public class TransitionSet extends Transition {
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 2792dcf..6fc77df 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -48,6 +48,7 @@
</ImageView>
<TextView
+ android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
@@ -56,6 +57,7 @@
</TextView>
<ProgressBar
+ android:id="@+id/progress_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index d4ce1cf..ee62f9f 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -21,14 +21,14 @@
android:title="@string/search"
android:icon="@*android:drawable/ic_menu_search_holo_light"
android:actionViewClass="android.widget.SearchView"
- android:showAsAction="ifRoom"
+ android:showAsAction="ifRoom|collapseActionView"
android:alphabeticShortcut="f"
android:imeOptions="actionSearch">
</item>
<item
android:id="@+id/action_add_printer"
- android:title="@null"
+ android:title="@string/print_add_printer"
android:icon="@*android:drawable/create_contact"
android:showAsAction="ifRoom"
android:alphabeticShortcut="a">
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 3a888a8..c82a20e 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -67,6 +67,9 @@
<!-- Title for the open all printers UI option in the printer list. [CHAR LIMIT=30] -->
<string name="all_printers">All printers…</string>
+ <!-- Title for the print dialog announced to the user for accessibility. Not shown in the UI. [CHAR LIMIT=none] -->
+ <string name="print_dialog">Print dialog</string>
+
<!-- Select printer activity -->
<!-- Title for the share action bar menu item. [CHAR LIMIT=20] -->
@@ -78,14 +81,32 @@
<!-- Title of the button to install a print service. [CHAR LIMIT=25] -->
<string name="add_print_service_label">Add service</string>
+ <!-- Utterance to announce that the search box is shown. This is spoken to a blind user. [CHAR LIMIT=none] -->
+ <string name="print_search_box_shown_utterance">Search box shown</string>
+
+ <!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
+ <string name="print_search_box_hidden_utterance">Search box hidden</string>
+
+ <!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
+ <string name="print_add_printer">Add printer</string>
+
+ <!-- Utterance to announce a change in the number of matches during a search. This is spoken to a blind user. [CHAR LIMIT=none] -->
+ <plurals name="print_search_result_count_utterance">
+ <item quantity="one"><xliff:g id="count" example="1">%1$s</xliff:g> printer found</item>
+ <item quantity="other"><xliff:g id="count" example="2">%1$s</xliff:g> printers found</item>
+ </plurals>
+
<!-- Add printer dialog -->
<!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
<string name="choose_print_service">Choose print service</string>
- <!-- Title for the prompt shown as a placeholder if no printers are found while searching. [CHAR LIMIT=50] -->
+ <!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
<string name="print_searching_for_printers">Searching for printers</string>
+ <!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
+ <string name="print_no_printers">No printers found</string>
+
<!-- Notifications -->
<!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
index f6db6be..d64380a 100644
--- a/packages/PrintSpooler/res/values/styles.xml
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -30,7 +30,6 @@
</style>
<style name="PrintOptionEditTextStyle">
- <item name="android:selectAllOnFocus">true</item>
<item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 65af830..8aa290c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -63,7 +63,6 @@
private static final boolean DEBUG = false;
private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
-
private static final int MAX_HISTORY_LENGTH = 50;
private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
@@ -388,25 +387,25 @@
+ FusedPrintersProvider.this.hashCode());
}
- // Ignore printer records whose target services are not installed.
+ // Ignore printer records whose target services are not enabled.
PrintManager printManager = (PrintManager) getContext()
.getSystemService(Context.PRINT_SERVICE);
List<PrintServiceInfo> services = printManager
- .getInstalledPrintServices();
+ .getEnabledPrintServices();
- Set<ComponentName> installedComponents = new ArraySet<ComponentName>();
+ Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
final int installedServiceCount = services.size();
for (int i = 0; i < installedServiceCount; i++) {
ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
ComponentName componentName = new ComponentName(
serviceInfo.packageName, serviceInfo.name);
- installedComponents.add(componentName);
+ enabledComponents.add(componentName);
}
final int printerCount = printers.size();
for (int i = printerCount - 1; i >= 0; i--) {
ComponentName printerServiceName = printers.get(i).getId().getServiceName();
- if (!installedComponents.contains(printerServiceName.getPackageName())) {
+ if (!enabledComponents.contains(printerServiceName)) {
printers.remove(i);
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index f1678ff..7f9be6c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -178,6 +178,8 @@
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
+ setTitle(R.string.print_dialog);
+
Bundle extras = getIntent().getExtras();
PrintJobInfo printJob = extras.getParcelable(EXTRA_PRINT_JOB);
@@ -1214,7 +1216,7 @@
if (capabilitiesChanged || statusChanged) {
// If something changed during update...
- if (updateUi()) {
+ if (updateUi() || !mController.hasPerformedLayout()) {
// Update the document.
mController.update();
}
@@ -1269,10 +1271,6 @@
showUi(UI_EDITING_PRINT_JOB, null);
bindUi();
-
- mCurrentPrinter = mDestinationSpinnerAdapter.mFakePdfPrinter;
- updatePrintAttributes(mCurrentPrinter.getCapabilities());
-
updateUi();
}
@@ -2001,11 +1999,10 @@
implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{
private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
- private final PrinterInfo mFakePdfPrinter;
+ private PrinterInfo mFakePdfPrinter;
public DestinationAdapter() {
getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
- mFakePdfPrinter = createFakePdfPrinter();
}
public int getPrinterIndex(PrinterId printerId) {
@@ -2039,7 +2036,9 @@
@Override
public int getCount() {
- return Math.min(mPrinters.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT);
+ final int additionalItemCount = (mFakePdfPrinter != null) ? 2 : 1;
+ return Math.min(mPrinters.size() + additionalItemCount,
+ DEST_ADAPTER_MAX_ITEM_COUNT);
}
@Override
@@ -2055,14 +2054,14 @@
@Override
public Object getItem(int position) {
if (mPrinters.isEmpty()) {
- if (position == 0) {
+ if (position == 0 && mFakePdfPrinter != null) {
return mFakePdfPrinter;
}
} else {
if (position < 1) {
return mPrinters.get(position);
}
- if (position == 1) {
+ if (position == 1 && mFakePdfPrinter != null) {
return mFakePdfPrinter;
}
if (position < getCount() - 1) {
@@ -2075,14 +2074,14 @@
@Override
public long getItemId(int position) {
if (mPrinters.isEmpty()) {
- if (position == 0) {
+ if (position == 0 && mFakePdfPrinter != null) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
}
if (position == 1) {
return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
}
} else {
- if (position == 1) {
+ if (position == 1 && mFakePdfPrinter != null) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
}
if (position == getCount() - 1) {
@@ -2114,14 +2113,14 @@
Drawable icon = null;
if (mPrinters.isEmpty()) {
- if (position == 0) {
+ if (position == 0 && mFakePdfPrinter != null) {
PrinterInfo printer = (PrinterInfo) getItem(position);
title = printer.getName();
} else if (position == 1) {
title = getString(R.string.all_printers);
}
} else {
- if (position == 1) {
+ if (position == 1 && mFakePdfPrinter != null) {
PrinterInfo printer = (PrinterInfo) getItem(position);
title = printer.getName();
} else if (position == getCount() - 1) {
@@ -2174,6 +2173,16 @@
@Override
public void onLoadFinished(Loader<List<PrinterInfo>> loader,
List<PrinterInfo> printers) {
+ // If this is the first load, create the fake PDF printer.
+ // We do this to avoid flicker where the PDF printer is the
+ // only one and as soon as the loader loads the favorites
+ // it gets switched. Not a great user experience.
+ if (mFakePdfPrinter == null) {
+ mCurrentPrinter = mFakePdfPrinter = createFakePdfPrinter();
+ updatePrintAttributes(mCurrentPrinter.getCapabilities());
+ updateUi();
+ }
+
// We rearrange the printers if the user selects a printer
// not shown in the initial short list. Therefore, we have
// to keep the printer order.
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
index 114c151..b8a9417 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -36,6 +36,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -43,6 +44,7 @@
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
@@ -80,6 +82,8 @@
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
new ArrayList<PrintServiceInfo>();
+ private AnnounceFilterResult mAnnounceFilterResult;
+
public static interface OnPrinterSelectedListener {
public void onPrinterSelected(PrinterId printerId);
}
@@ -93,9 +97,23 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- setListAdapter(new DestinationAdapter());
- View emptyView = getActivity().findViewById(R.id.empty_print_state);
- getListView().setEmptyView(emptyView);
+ final DestinationAdapter adapter = new DestinationAdapter();
+ adapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ if (!getActivity().isFinishing() && adapter.getCount() <= 0) {
+ updateEmptyView(adapter);
+ }
+ }
+
+ @Override
+ public void onInvalidated() {
+ if (!getActivity().isFinishing()) {
+ updateEmptyView(adapter);
+ }
+ }
+ });
+ setListAdapter(adapter);
}
@Override
@@ -117,6 +135,18 @@
return true;
}
});
+ searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_shown_utterance));
+ }
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_hidden_utterance));
+ }
+ });
if (mAddPrinterServices.isEmpty()) {
menu.removeItem(R.id.action_add_printer);
@@ -213,10 +243,32 @@
transaction.commit();
}
+ public void updateEmptyView(DestinationAdapter adapter) {
+ if (getListView().getEmptyView() == null) {
+ View emptyView = getActivity().findViewById(R.id.empty_print_state);
+ getListView().setEmptyView(emptyView);
+ }
+ TextView titleView = (TextView) getActivity().findViewById(R.id.title);
+ View progressBar = getActivity().findViewById(R.id.progress_bar);
+ if (adapter.getUnfilteredCount() <= 0) {
+ titleView.setText(R.string.print_searching_for_printers);
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ titleView.setText(R.string.print_no_printers);
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+
+ private void announceSearchResult() {
+ if (mAnnounceFilterResult == null) {
+ mAnnounceFilterResult = new AnnounceFilterResult();
+ }
+ mAnnounceFilterResult.post();
+ }
+
public static class AddPrinterAlertDialogFragment extends DialogFragment {
- private static final String DEFAULT_MARKET_QUERY_STRING =
- "market://search?q=print";
+ private String mAddPrintServiceItem;
@Override
@SuppressWarnings("unchecked")
@@ -227,47 +279,56 @@
final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
getArguments().getParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS);
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
- android.R.layout.simple_list_item_1);
+ final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
+ getActivity(), android.R.layout.simple_list_item_1);
final int printServiceCount = printServices.size();
for (int i = 0; i < printServiceCount; i++) {
PrintServiceInfo printService = printServices.get(i);
adapter.add(printService.getResolveInfo().loadLabel(
getActivity().getPackageManager()).toString());
}
+ final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
+ Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+ final Intent marketIntent;
+ if (!TextUtils.isEmpty(searchUri)) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+ if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
+ marketIntent = intent;
+ mAddPrintServiceItem = getString(R.string.add_print_service_label);
+ adapter.add(mAddPrintServiceItem);
+ } else {
+ marketIntent = null;
+ }
+ } else {
+ marketIntent = null;
+ }
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- PrintServiceInfo printService = printServices.get(which);
- ComponentName componentName = new ComponentName(
- printService.getResolveInfo().serviceInfo.packageName,
- printService.getAddPrintersActivityName());
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(componentName);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
+ String item = adapter.getItem(which);
+ if (item == mAddPrintServiceItem) {
+ try {
+ startActivity(marketIntent);
+ } catch (ActivityNotFoundException anfe) {
+ Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
+ }
+ } else {
+ PrintServiceInfo printService = printServices.get(which);
+ ComponentName componentName = new ComponentName(
+ printService.getResolveInfo().serviceInfo.packageName,
+ printService.getAddPrintersActivityName());
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(componentName);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException anfe) {
+ Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
+ }
}
}
});
- Uri marketUri = Uri.parse(DEFAULT_MARKET_QUERY_STRING);
- final Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
- if (getActivity().getPackageManager().resolveActivity(marketIntent, 0) != null) {
- builder.setPositiveButton(R.string.add_print_service_label,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- try {
- startActivity(marketIntent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
- }
- }
- });
- }
-
return builder.create();
}
}
@@ -315,7 +376,9 @@
@Override
@SuppressWarnings("unchecked")
protected void publishResults(CharSequence constraint, FilterResults results) {
+ final boolean resultCountChanged;
synchronized (mLock) {
+ final int oldPrinterCount = mFilteredPrinters.size();
mLastSearchString = constraint;
mFilteredPrinters.clear();
if (results == null) {
@@ -324,12 +387,22 @@
List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
mFilteredPrinters.addAll(printers);
}
+ resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
+ }
+ if (resultCountChanged) {
+ announceSearchResult();
}
notifyDataSetChanged();
}
};
}
+ public int getUnfilteredCount() {
+ synchronized (mLock) {
+ return mPrinters.size();
+ }
+ }
+
@Override
public int getCount() {
synchronized (mLock) {
@@ -434,4 +507,30 @@
notifyDataSetInvalidated();
}
}
+
+ private final class AnnounceFilterResult implements Runnable {
+ private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+ public void post() {
+ remove();
+ getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+ }
+
+ public void remove() {
+ getListView().removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ final int count = getListView().getAdapter().getCount();
+ final String text;
+ if (count <= 0) {
+ text = getString(R.string.print_no_printers);
+ } else {
+ text = getActivity().getResources().getQuantityString(
+ R.plurals.print_search_result_count_utterance, count, count);
+ }
+ getListView().announceForAccessibility(text);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 04885f0..6e53363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -391,7 +391,7 @@
final AccessibilityManager accessibilityManager =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
+ if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
// In accessibility mode, we add a simple click handler since swipe is tough to
// trigger near screen edges.
View camera = getCameraButton();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f23bcba..fe91b6c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3193,6 +3193,7 @@
if (task.userId == tr.userId
&& ((task.affinity != null && task.affinity.equals(tr.affinity))
|| (task.intent != null && task.intent.filterEquals(tr.intent)))) {
+ tr.disposeThumbnail();
mRecentTasks.remove(i);
i--;
N--;
@@ -3204,7 +3205,7 @@
}
}
if (N >= MAX_RECENT_TASKS) {
- mRecentTasks.remove(N-1);
+ mRecentTasks.remove(N-1).disposeThumbnail();
}
mRecentTasks.add(0, task);
}
@@ -3464,7 +3465,8 @@
clearProfilerLocked();
}
- mStackSupervisor.handleAppDiedLocked(app, restarting);
+ // Remove this application's activities from active lists.
+ boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app, restarting);
app.activities.clear();
@@ -3475,6 +3477,19 @@
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
}
+
+ if (!restarting) {
+ if (!mStackSupervisor.resumeTopActivitiesLocked()) {
+ // If there was nothing to resume, and we are not already
+ // restarting this process, but there is a visible activity that
+ // is hosted by the process... then make sure all visible
+ // activities are running, taking care of restarting this
+ // process.
+ if (hasVisibleActivities) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ }
+ }
+ }
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
@@ -6738,6 +6753,7 @@
}
private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
+ tr.disposeThumbnail();
mRecentTasks.remove(tr);
mStackSupervisor.removeTask(tr);
final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
@@ -15818,9 +15834,11 @@
}
}
- boolean haveActivities = mStackSupervisor.switchUserLocked(userId, uss);
- if (!haveActivities) {
+ boolean homeInFront = mStackSupervisor.switchUserLocked(userId, uss);
+ if (homeInFront) {
startHomeActivityLocked(userId);
+ } else {
+ mStackSupervisor.resumeTopActivitiesLocked();
}
EventLogTags.writeAmSwitchUser(userId);
@@ -16146,6 +16164,8 @@
} catch (RemoteException e) {
}
}
+
+ mStackSupervisor.removeUserLocked(userId);
}
@Override
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 2c0b83b..cf686672 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -572,8 +572,8 @@
}
boolean isRootActivity() {
- ArrayList<ActivityRecord> activities = task.mActivities;
- return activities.size() == 0 || this == task.mActivities.get(0);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ return activities.size() == 0 || this == activities.get(0);
}
UriPermissionOwner getUriPermissionsLocked() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 45b30f1..fc83f05 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -549,31 +549,26 @@
/*
* Move the activities around in the stack to bring a user to the foreground.
- * @return whether there are any activities for the specified user.
*/
- final boolean switchUserLocked(int userId) {
+ final void switchUserLocked(int userId) {
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
if (mCurrentUser == userId) {
- return true;
+ return;
}
mCurrentUser = userId;
// Move userId's tasks to the top.
- boolean haveActivities = false;
int index = mTaskHistory.size();
for (int i = 0; i < index; ++i) {
TaskRecord task = mTaskHistory.get(i);
if (task.userId == userId) {
- haveActivities = true;
mTaskHistory.remove(i);
mTaskHistory.add(task);
--index;
}
}
-
- return haveActivities;
}
void minimalResumeActivityLocked(ActivityRecord r) {
@@ -678,6 +673,14 @@
return null;
}
+ TaskRecord tr = who.task;
+ if (tr.intent != null && (tr.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
+ // If this task is being excluded from recents, we don't want to take
+ // the expense of capturing a thumbnail, since we will never show it.
+ return null;
+ }
+
Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
@@ -690,6 +693,7 @@
if (w > 0) {
if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
+ || mLastScreenshotActivity.state == ActivityState.RESUMED
|| mLastScreenshotBitmap.getWidth() != w
|| mLastScreenshotBitmap.getHeight() != h) {
mLastScreenshotActivity = who;
@@ -1092,7 +1096,7 @@
int rIndex = task.mActivities.indexOf(r);
for ( --rIndex; rIndex >= 0; --rIndex) {
final ActivityRecord blocker = task.mActivities.get(rIndex);
- if (!blocker.finishing && blocker.visible) {
+ if (!blocker.finishing) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Home visibility for " +
r + " blocked by " + blocker);
break;
@@ -1623,6 +1627,12 @@
}
private void insertTaskAtTop(TaskRecord task) {
+ // If this is being moved to the top by another activity or being launched from the home
+ // activity, set mOnTopOfHome accordingly.
+ final boolean fromHome = mStackSupervisor.getLastStack().isHomeStack();
+ if (!isHomeStack() && (fromHome || topTask() != task)) {
+ task.mOnTopOfHome = fromHome;
+ }
mTaskHistory.remove(task);
// Now put task at top.
int stackNdx = mTaskHistory.size();
@@ -3421,13 +3431,9 @@
/**
* Reset local parameters because an app's activity died.
* @param app The app of the activity that died.
- * @return true if home should be launched next.
+ * @return result from removeHistoryRecordsForAppLocked.
*/
boolean handleAppDiedLocked(ProcessRecord app) {
- if (!containsApp(app)) {
- return false;
- }
-
if (mPausingActivity != null && mPausingActivity.app == app) {
if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
"App died while pausing: " + mPausingActivity);
@@ -3438,30 +3444,7 @@
mLastNoHistoryActivity = null;
}
- // Determine if the top task is exiting and should return to home. Do this before it gets
- // removed in removeHistoryRecordsForAppsLocked.
- boolean launchHomeNext = false;
- TaskRecord topTask = mTaskHistory.get(mTaskHistory.size() - 1);
- ArrayList<ActivityRecord> activities = topTask.mActivities;
- int activityNdx;
- for (activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
- if (r.finishing) {
- continue;
- }
- if (r.app != app) {
- // This is the dying activity.
- break;
- }
- }
- if (activityNdx < 0) {
- // All activities in task belong to app. Set launchHomeNext to task's value.
- launchHomeNext = topTask.mOnTopOfHome;
- }
-
- removeHistoryRecordsForAppLocked(app);
-
- return launchHomeNext;
+ return removeHistoryRecordsForAppLocked(app);
}
void handleAppCrashLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 2b69a4e..b4de258 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -70,6 +70,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.TransferPipe;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -203,6 +204,9 @@
*/
final PowerManager.WakeLock mGoingToSleep;
+ /** State of the stacks when user switched, indexed by userId. */
+ SparseBooleanArray mUserHomeInFront = new SparseBooleanArray(2);
+
public ActivityStackSupervisor(ActivityManagerService service, Context context,
Looper looper) {
mService = service;
@@ -288,22 +292,6 @@
return mService.startHomeActivityLocked(mCurrentUser);
}
- final void setLaunchHomeTaskNextFlag(ActivityRecord sourceRecord, ActivityRecord r,
- ActivityStack stack) {
- if (stack == mHomeStack) {
- return;
- }
- if ((sourceRecord == null && getLastStack() == mHomeStack) ||
- (sourceRecord != null && sourceRecord.isHomeActivity())) {
- if (r == null) {
- r = stack.topRunningActivityLocked(null);
- }
- if (r != null && !r.isHomeActivity() && r.isRootActivity()) {
- r.task.mOnTopOfHome = true;
- }
- }
- }
-
void setDismissKeyguard(boolean dismiss) {
if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen(" dismiss=" + dismiss);
mDismissKeyguardOnNextActivity = dismiss;
@@ -1479,7 +1467,6 @@
// is the case, so this is it! And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
- setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
resumeTopActivitiesLocked(targetStack, null, options);
} else {
ActivityOptions.abort(options);
@@ -1575,9 +1562,6 @@
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
- // Reset flag so it gets correctly reevaluated.
- intentActivity.task.mOnTopOfHome = false;
- setLaunchHomeTaskNextFlag(sourceRecord, intentActivity, targetStack);
targetStack.resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
@@ -1615,7 +1599,6 @@
// resumed the top activity.
topStack.mLastPausedActivity = null;
if (doResume) {
- setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
resumeTopActivitiesLocked();
}
ActivityOptions.abort(options);
@@ -1694,7 +1677,6 @@
// resumed the top activity.
targetStack.mLastPausedActivity = null;
if (doResume) {
- setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
targetStack.resumeTopActivityLocked(null);
}
ActivityOptions.abort(options);
@@ -1717,7 +1699,6 @@
top.deliverNewIntentLocked(callingUid, r.intent);
targetStack.mLastPausedActivity = null;
if (doResume) {
- setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
targetStack.resumeTopActivityLocked(null);
}
return ActivityManager.START_DELIVERED_TO_TOP;
@@ -1751,7 +1732,6 @@
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
}
ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack);
targetStack.mLastPausedActivity = null;
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
mService.setFocusedActivityLocked(r);
@@ -1926,30 +1906,12 @@
return r;
}
- void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
- boolean launchHomeTaskNext = false;
- final ActivityStack focusedStack = getFocusedStack();
- final int numStacks = mStacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = mStacks.get(stackNdx);
- // Only update launchHomeTaskNext for the focused stack.
- launchHomeTaskNext |= (stack.handleAppDiedLocked(app) && stack == focusedStack);
+ boolean handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+ boolean hasVisibleActivities = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ hasVisibleActivities |= mStacks.get(stackNdx).handleAppDiedLocked(app);
}
-
- if (!restarting) {
- if (launchHomeTaskNext) {
- resumeHomeActivity(null);
- } else {
- if (!resumeTopActivitiesLocked(focusedStack, null, null)) {
- // If there was nothing to resume, and we are not already
- // restarting this process, but there is a visible activity that
- // is hosted by the process... then make sure all visible
- // activities are running, taking care of restarting this
- // process.
- ensureActivitiesVisibleLocked(null, 0);
- }
- }
- }
+ return hasVisibleActivities;
}
void closeSystemDialogsLocked() {
@@ -1960,6 +1922,10 @@
}
}
+ void removeUserLocked(int userId) {
+ mUserHomeInFront.delete(userId);
+ }
+
/**
* @return true if some activity was finished (or would have finished if doit were true).
*/
@@ -2278,17 +2244,17 @@
}
boolean switchUserLocked(int userId, UserStartedState uss) {
+ mUserHomeInFront.put(mCurrentUser, isFrontStack(mHomeStack));
+ final boolean homeInFront = mUserHomeInFront.get(userId, true);
mCurrentUser = userId;
mStartingUsers.add(uss);
- boolean haveActivities = false;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- haveActivities |= mStacks.get(stackNdx).switchUserLocked(userId);
+ mStacks.get(stackNdx).switchUserLocked(userId);
}
- resumeTopActivitiesLocked();
-
- return haveActivities;
+ moveHomeStack(homeInFront);
+ return homeInFront;
}
final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
@@ -2381,6 +2347,7 @@
pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
+ pw.print(prefix); pw.println("mUserHomeInFront: " + mUserHomeInFront);
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 385253e..3d568ff 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -128,6 +128,16 @@
}
}
+ void disposeThumbnail() {
+ super.disposeThumbnail();
+ for (int i=mActivities.size()-1; i>=0; i--) {
+ ThumbnailHolder thumb = mActivities.get(i).thumbHolder;
+ if (thumb != this) {
+ thumb.disposeThumbnail();
+ }
+ }
+ }
+
ActivityRecord getTopActivity() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
index 02f4fcb..a6974f5 100644
--- a/services/java/com/android/server/am/ThumbnailHolder.java
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -21,4 +21,9 @@
public class ThumbnailHolder {
Bitmap lastThumbnail; // Last thumbnail captured for this item.
CharSequence lastDescription; // Last description captured for this item.
+
+ void disposeThumbnail() {
+ lastThumbnail = null;
+ lastDescription = null;
+ }
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f093f2b..e84f90e 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -850,6 +850,19 @@
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
res.pkg.applicationInfo.packageName, null, updateUsers);
+
+ // treat asec-hosted packages like removable media on upgrade
+ if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + res.pkg
+ + " is ASEC-hosted -> AVAILABLE");
+ }
+ int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
+ ArrayList<String> pkgList = new ArrayList<String>(1);
+ pkgList.add(res.pkg.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(true, false,
+ pkgList,uidArray, null);
+ }
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -4643,6 +4656,20 @@
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // If the package lives in an asec, tell everyone that the container is going
+ // away so they can clean up any references to its resources (which would prevent
+ // vold from being able to unmount the asec)
+ if (isForwardLocked(pkg) || isExternal(pkg)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + pkg + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[] { pkg.applicationInfo.uid };
+ final ArrayList<String> pkgList = new ArrayList<String>(1);
+ pkgList.add(pkg.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
+
+ // Post the request that it be killed now that the going-away broadcast is en route
killApplication(pkg.applicationInfo.packageName,
pkg.applicationInfo.uid, "update pkg");
}
@@ -9120,6 +9147,17 @@
}
}
+ boolean locationIsPrivileged(File path) {
+ try {
+ final String privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app")
+ .getCanonicalPath();
+ return path.getCanonicalPath().startsWith(privilegedAppDir);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to access code path " + path);
+ }
+ return false;
+ }
+
/*
* Tries to delete system package.
*/
@@ -9175,9 +9213,12 @@
}
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+ int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
+ if (locationIsPrivileged(disabledPs.codePath)) {
+ parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+ }
PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
- PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
- SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
+ parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name
@@ -10731,8 +10772,8 @@
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, ArrayList<String> pkgList,
- int uidArr[], IIntentReceiver finishedReceiver) {
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
int size = pkgList.size();
if (size > 0) {
// Send broadcasts here
@@ -10742,6 +10783,9 @@
if (uidArr != null) {
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
+ if (replacing && !mediaStatus) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
+ }
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
@@ -10844,7 +10888,7 @@
}
// Send a broadcast to let everyone know we are done processing
if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
}
// Force gc to avoid any stale parser references that we might have.
if (doGc) {
@@ -10921,7 +10965,8 @@
// broadcast when packages get disabled, force a gc to clean things up.
// and unload all the containers.
if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
+ sendResourcesChangedBroadcast(false, false, pkgList, uidArr,
+ new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky,
int sendingUser) throws RemoteException {
@@ -11041,7 +11086,7 @@
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
// Send resources unavailable broadcast
- sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArr, null);
// Update package code and resource paths
synchronized (mInstallLock) {
synchronized (mPackages) {
@@ -11119,7 +11164,7 @@
}
}
// Send resources available broadcast
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
}
}
if (returnCode != PackageManager.MOVE_SUCCEEDED) {