am a7d868d4
: Merge "Add toast when an app intercepts the launch of another app." into gingerbread
Merge commit 'a7d868d4f99dfaf85e13498210aecf1ad8efd859' into gingerbread-plus-aosp * commit 'a7d868d4f99dfaf85e13498210aecf1ad8efd859': Add toast when an app intercepts the launch of another app.
This commit is contained in:
65
core/res/res/layout/launch_warning.xml
Normal file
65
core/res/res/layout/launch_warning.xml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?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:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:gravity="fill_horizontal"
|
||||||
|
android:src="@android:drawable/divider_horizontal_dark" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="10dip"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<ImageView android:id="@+id/replace_app_icon"
|
||||||
|
android:layout_width="@android:dimen/app_icon_size"
|
||||||
|
android:layout_height="@android:dimen/app_icon_size"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
<TextView android:id="@+id/replace_message"
|
||||||
|
style="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="5dip" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="10dip"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<ImageView android:id="@+id/original_app_icon"
|
||||||
|
android:layout_width="@android:dimen/app_icon_size"
|
||||||
|
android:layout_height="@android:dimen/app_icon_size"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
<TextView android:id="@+id/original_message"
|
||||||
|
style="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="5dip" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -1921,6 +1921,12 @@
|
|||||||
<string name="report">Report</string>
|
<string name="report">Report</string>
|
||||||
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
|
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
|
||||||
<string name="wait">Wait</string>
|
<string name="wait">Wait</string>
|
||||||
|
<!-- [CHAR LIMIT=25] Title of the alert when application launches on top of another. -->
|
||||||
|
<string name="launch_warning_title">Application redirected</string>
|
||||||
|
<!-- [CHAR LIMIT=50] Title of the alert when application launches on top of another. -->
|
||||||
|
<string name="launch_warning_replace"><xliff:g id="app_name">%1$s</xliff:g> is now running.</string>
|
||||||
|
<!-- [CHAR LIMIT=50] Title of the alert when application launches on top of another. -->
|
||||||
|
<string name="launch_warning_original"><xliff:g id="app_name">%1$s</xliff:g> was originally launched.</string>
|
||||||
|
|
||||||
<!-- Text of the alert that is displayed when an application has violated StrictMode. -->
|
<!-- Text of the alert that is displayed when an application has violated StrictMode. -->
|
||||||
<string name="smv_application">The application <xliff:g id="application">%1$s</xliff:g>
|
<string name="smv_application">The application <xliff:g id="application">%1$s</xliff:g>
|
||||||
|
@ -521,5 +521,12 @@
|
|||||||
<item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item>
|
<item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item>
|
||||||
<item name="android:textColor">@android:color/secondary_text_nofocus</item>
|
<item name="android:textColor">@android:color/secondary_text_nofocus</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- Default theme for window that looks like a toast. -->
|
||||||
|
<style name="Theme.Toast" parent="@android:style/Theme.Dialog">
|
||||||
|
<item name="android:windowBackground">@android:drawable/toast_frame</item>
|
||||||
|
<item name="android:windowAnimationStyle">@android:style/Animation.Toast</item>
|
||||||
|
<item name="android:backgroundDimEnabled">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -753,6 +753,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
boolean mWaitingUpdate = false;
|
boolean mWaitingUpdate = false;
|
||||||
boolean mDidUpdate = false;
|
boolean mDidUpdate = false;
|
||||||
boolean mOnBattery = false;
|
boolean mOnBattery = false;
|
||||||
|
boolean mLaunchWarningShown = false;
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
@ -2903,6 +2904,30 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
|
||||||
|
if (!mLaunchWarningShown) {
|
||||||
|
mLaunchWarningShown = true;
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (ActivityManagerService.this) {
|
||||||
|
final Dialog d = new LaunchWarningWindow(mContext, cur, next);
|
||||||
|
d.show();
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (ActivityManagerService.this) {
|
||||||
|
d.dismiss();
|
||||||
|
mLaunchWarningShown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final void decPersistentCountLocked(ProcessRecord app) {
|
final void decPersistentCountLocked(ProcessRecord app) {
|
||||||
app.persistentActivities--;
|
app.persistentActivities--;
|
||||||
if (app.persistentActivities > 0) {
|
if (app.persistentActivities > 0) {
|
||||||
|
@ -34,6 +34,7 @@ import android.os.SystemClock;
|
|||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.TimeUtils;
|
||||||
import android.view.IApplicationToken;
|
import android.view.IApplicationToken;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@ -68,7 +69,8 @@ class ActivityRecord extends IApplicationToken.Stub {
|
|||||||
int icon; // resource identifier of activity's icon.
|
int icon; // resource identifier of activity's icon.
|
||||||
int theme; // resource identifier of activity's theme.
|
int theme; // resource identifier of activity's theme.
|
||||||
TaskRecord task; // the task this is in.
|
TaskRecord task; // the task this is in.
|
||||||
long startTime; // when we starting launching this activity
|
long launchTime; // when we starting launching this activity
|
||||||
|
long startTime; // last time this activity was started
|
||||||
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
|
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
|
||||||
Configuration configuration; // configuration activity was last running in
|
Configuration configuration; // configuration activity was last running in
|
||||||
ActivityRecord resultTo; // who started this entry, so will get our reply
|
ActivityRecord resultTo; // who started this entry, so will get our reply
|
||||||
@ -165,6 +167,11 @@ class ActivityRecord extends IApplicationToken.Stub {
|
|||||||
pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
|
pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
|
||||||
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
|
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
|
||||||
pw.print(" idle="); pw.println(idle);
|
pw.print(" idle="); pw.println(idle);
|
||||||
|
if (launchTime != 0 || startTime != 0) {
|
||||||
|
pw.print(prefix); pw.print("launchTime=");
|
||||||
|
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
|
||||||
|
TimeUtils.formatDuration(startTime, pw); pw.println("");
|
||||||
|
}
|
||||||
if (waitingVisible || nowVisible) {
|
if (waitingVisible || nowVisible) {
|
||||||
pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
|
pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
|
||||||
pw.print(" nowVisible="); pw.println(nowVisible);
|
pw.print(" nowVisible="); pw.println(nowVisible);
|
||||||
@ -417,9 +424,9 @@ class ActivityRecord extends IApplicationToken.Stub {
|
|||||||
|
|
||||||
public void windowsVisible() {
|
public void windowsVisible() {
|
||||||
synchronized(service) {
|
synchronized(service) {
|
||||||
if (startTime != 0) {
|
if (launchTime != 0) {
|
||||||
final long curTime = SystemClock.uptimeMillis();
|
final long curTime = SystemClock.uptimeMillis();
|
||||||
final long thisTime = curTime - startTime;
|
final long thisTime = curTime - launchTime;
|
||||||
final long totalTime = stack.mInitialStartTime != 0
|
final long totalTime = stack.mInitialStartTime != 0
|
||||||
? (curTime - stack.mInitialStartTime) : thisTime;
|
? (curTime - stack.mInitialStartTime) : thisTime;
|
||||||
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
|
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
|
||||||
@ -428,22 +435,24 @@ class ActivityRecord extends IApplicationToken.Stub {
|
|||||||
thisTime, totalTime);
|
thisTime, totalTime);
|
||||||
StringBuilder sb = service.mStringBuilder;
|
StringBuilder sb = service.mStringBuilder;
|
||||||
sb.setLength(0);
|
sb.setLength(0);
|
||||||
sb.append("Displayed activity ");
|
sb.append("Displayed ");
|
||||||
sb.append(shortComponentName);
|
sb.append(shortComponentName);
|
||||||
sb.append(": ");
|
sb.append(": ");
|
||||||
sb.append(thisTime);
|
TimeUtils.formatDuration(thisTime, sb);
|
||||||
sb.append(" ms (total ");
|
sb.append(" (total ");
|
||||||
|
TimeUtils.formatDuration(totalTime, sb);
|
||||||
sb.append(totalTime);
|
sb.append(totalTime);
|
||||||
sb.append(" ms)");
|
sb.append(")");
|
||||||
Log.i(ActivityManagerService.TAG, sb.toString());
|
Log.i(ActivityManagerService.TAG, sb.toString());
|
||||||
}
|
}
|
||||||
stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
|
stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
|
||||||
if (totalTime > 0) {
|
if (totalTime > 0) {
|
||||||
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
|
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
|
||||||
}
|
}
|
||||||
startTime = 0;
|
launchTime = 0;
|
||||||
stack.mInitialStartTime = 0;
|
stack.mInitialStartTime = 0;
|
||||||
}
|
}
|
||||||
|
startTime = 0;
|
||||||
stack.reportActivityVisibleLocked(this);
|
stack.reportActivityVisibleLocked(this);
|
||||||
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
|
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
|
||||||
ActivityManagerService.TAG, "windowsVisible(): " + this);
|
ActivityManagerService.TAG, "windowsVisible(): " + this);
|
||||||
|
@ -102,6 +102,10 @@ public class ActivityStack {
|
|||||||
// 30 minutes.
|
// 30 minutes.
|
||||||
static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
|
static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
|
||||||
|
|
||||||
|
// How long between activity launches that we consider safe to not warn
|
||||||
|
// the user about an unexpected activity being launched on top.
|
||||||
|
static final long START_WARN_TIME = 5*1000;
|
||||||
|
|
||||||
// Set to false to disable the preview that is shown while a new activity
|
// Set to false to disable the preview that is shown while a new activity
|
||||||
// is being started.
|
// is being started.
|
||||||
static final boolean SHOW_APP_STARTING_PREVIEW = true;
|
static final boolean SHOW_APP_STARTING_PREVIEW = true;
|
||||||
@ -212,6 +216,13 @@ public class ActivityStack {
|
|||||||
*/
|
*/
|
||||||
ActivityRecord mResumedActivity = null;
|
ActivityRecord mResumedActivity = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the last activity that has been started. It is only used to
|
||||||
|
* identify when multiple activities are started at once so that the user
|
||||||
|
* can be warned they may not be in the activity they think they are.
|
||||||
|
*/
|
||||||
|
ActivityRecord mLastStartedActivity = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set when we know we are going to be calling updateConfiguration()
|
* Set when we know we are going to be calling updateConfiguration()
|
||||||
* soon, so want to skip intermediate config checks.
|
* soon, so want to skip intermediate config checks.
|
||||||
@ -586,10 +597,10 @@ public class ActivityStack {
|
|||||||
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
|
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
|
||||||
r.info.applicationInfo.uid);
|
r.info.applicationInfo.uid);
|
||||||
|
|
||||||
if (r.startTime == 0) {
|
if (r.launchTime == 0) {
|
||||||
r.startTime = SystemClock.uptimeMillis();
|
r.launchTime = SystemClock.uptimeMillis();
|
||||||
if (mInitialStartTime == 0) {
|
if (mInitialStartTime == 0) {
|
||||||
mInitialStartTime = r.startTime;
|
mInitialStartTime = r.launchTime;
|
||||||
}
|
}
|
||||||
} else if (mInitialStartTime == 0) {
|
} else if (mInitialStartTime == 0) {
|
||||||
mInitialStartTime = SystemClock.uptimeMillis();
|
mInitialStartTime = SystemClock.uptimeMillis();
|
||||||
@ -1090,6 +1101,31 @@ public class ActivityStack {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Okay we are now going to start a switch, to 'next'. We may first
|
||||||
|
// have to pause the current activity, but this is an important point
|
||||||
|
// where we have decided to go to 'next' so keep track of that.
|
||||||
|
if (mLastStartedActivity != null) {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
final boolean inTime = mLastStartedActivity.startTime != 0
|
||||||
|
&& (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
|
||||||
|
final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
|
||||||
|
final int nextUid = next.info.applicationInfo.uid;
|
||||||
|
if (inTime && lastUid != nextUid
|
||||||
|
&& lastUid != next.launchedFromUid
|
||||||
|
&& mService.checkPermission(
|
||||||
|
android.Manifest.permission.STOP_APP_SWITCHES,
|
||||||
|
-1, next.launchedFromUid)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
mService.showLaunchWarningLocked(mLastStartedActivity, next);
|
||||||
|
} else {
|
||||||
|
next.startTime = now;
|
||||||
|
mLastStartedActivity = next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next.startTime = SystemClock.uptimeMillis();
|
||||||
|
mLastStartedActivity = next;
|
||||||
|
}
|
||||||
|
|
||||||
// We need to start pausing the current activity so the top one
|
// We need to start pausing the current activity so the top one
|
||||||
// can be resumed...
|
// can be resumed...
|
||||||
if (mResumedActivity != null) {
|
if (mResumedActivity != null) {
|
||||||
@ -1314,7 +1350,6 @@ public class ActivityStack {
|
|||||||
|
|
||||||
if (!newTask) {
|
if (!newTask) {
|
||||||
// If starting in an existing task, find where that is...
|
// If starting in an existing task, find where that is...
|
||||||
ActivityRecord next = null;
|
|
||||||
boolean startIt = true;
|
boolean startIt = true;
|
||||||
for (int i = NH-1; i >= 0; i--) {
|
for (int i = NH-1; i >= 0; i--) {
|
||||||
ActivityRecord p = (ActivityRecord)mHistory.get(i);
|
ActivityRecord p = (ActivityRecord)mHistory.get(i);
|
||||||
@ -1342,14 +1377,13 @@ public class ActivityStack {
|
|||||||
if (p.fullscreen) {
|
if (p.fullscreen) {
|
||||||
startIt = false;
|
startIt = false;
|
||||||
}
|
}
|
||||||
next = p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place a new activity at top of stack, so it is next to interact
|
// Place a new activity at top of stack, so it is next to interact
|
||||||
// with the user.
|
// with the user.
|
||||||
if (addPos < 0) {
|
if (addPos < 0) {
|
||||||
addPos = mHistory.size();
|
addPos = NH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are not placing the new activity frontmost, we do not want
|
// If we are not placing the new activity frontmost, we do not want
|
||||||
|
36
services/java/com/android/server/am/LaunchWarningWindow.java
Normal file
36
services/java/com/android/server/am/LaunchWarningWindow.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.android.server.am;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public class LaunchWarningWindow extends Dialog {
|
||||||
|
public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
|
||||||
|
super(context, R.style.Theme_Toast);
|
||||||
|
|
||||||
|
requestWindowFeature(Window.FEATURE_LEFT_ICON);
|
||||||
|
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
|
||||||
|
|
||||||
|
setContentView(R.layout.launch_warning);
|
||||||
|
setTitle(context.getText(R.string.launch_warning_title));
|
||||||
|
getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
|
||||||
|
R.drawable.ic_dialog_alert);
|
||||||
|
ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
|
||||||
|
icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
|
||||||
|
TextView text = (TextView)findViewById(R.id.replace_message);
|
||||||
|
text.setText(context.getResources().getString(R.string.launch_warning_replace,
|
||||||
|
next.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
|
||||||
|
icon = (ImageView)findViewById(R.id.original_app_icon);
|
||||||
|
icon.setImageDrawable(cur.info.applicationInfo.loadIcon(context.getPackageManager()));
|
||||||
|
text = (TextView)findViewById(R.id.original_message);
|
||||||
|
text.setText(context.getResources().getString(R.string.launch_warning_original,
|
||||||
|
cur.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user