Immersive activity API.

An Activity can declare itself to be "immersive" either by
setting android:immersive="true" in AndroidManifest or by
calling setImmersive(true).

Immersive activities "should" not be interrupted, for
example by Notifications with an associated
fullScreenIntent. (In the future we may even prevent any
non-system application from successfully calling
startActivity() if the foreground activity is immersive.)
Notifications with FLAG_HIGH_PRIORITY set will be shown to
the user in some less-obtrusive way if the frontmost
activity is immersive.

Change-Id: I8d0c25cc4e22371c27cbf2bb6372d2c95d57b2d7
This commit is contained in:
Daniel Sandler
2010-06-23 16:29:36 -04:00
parent efbe2d78ee
commit 69a4817e3e
6 changed files with 170 additions and 0 deletions

View File

@ -19543,6 +19543,17 @@
visibility="public"
>
</method>
<method name="isImmersive"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isTaskRoot"
return="boolean"
abstract="false"
@ -20496,6 +20507,19 @@
<parameter name="uri" type="android.net.Uri">
</parameter>
</method>
<method name="setImmersive"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="i" type="boolean">
</parameter>
</method>
<method name="setIntent"
return="void"
abstract="false"

View File

@ -3720,6 +3720,46 @@ public class Activity extends ContextThemeWrapper
return null;
}
/**
* Bit indicating that this activity is "immersive" and should not be
* interrupted by notifications if possible.
*
* This value is initially set by the manifest property
* <code>android:immersive</code> but may be changed at runtime by
* {@link #setImmersive}.
*
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
*/
public boolean isImmersive() {
try {
return ActivityManagerNative.getDefault().isImmersive(mToken);
} catch (RemoteException e) {
return false;
}
}
/**
* Adjust the current immersive mode setting.
*
* Note that changing this value will have no effect on the activity's
* {@link android.content.pm.ActivityInfo} structure; that is, if
* <code>android:immersive</code> is set to <code>true</code>
* in the application's manifest entry for this activity, the {@link
* android.content.pm.ActivityInfo#flags ActivityInfo.flags} member will
* always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
* FLAG_IMMERSIVE} bit set.
*
* @see #isImmersive
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
*/
public void setImmersive(boolean i) {
try {
ActivityManagerNative.getDefault().setImmersive(mToken, i);
} catch (RemoteException e) {
// pass
}
}
// ------------------ Internal API ------------------
final void setParent(Activity parent) {

View File

@ -1268,6 +1268,31 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
case IS_IMMERSIVE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
reply.writeInt(isImmersive(token) ? 1 : 0);
reply.writeNoException();
return true;
}
case SET_IMMERSIVE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
boolean imm = data.readInt() == 1;
setImmersive(token, imm);
reply.writeNoException();
return true;
}
case IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
reply.writeInt(isTopActivityImmersive() ? 1 : 0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
@ -2802,5 +2827,45 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
public void setImmersive(IBinder token, boolean immersive)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeInt(immersive ? 1 : 0);
mRemote.transact(SET_IMMERSIVE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
public boolean isImmersive(IBinder token)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
mRemote.transact(IS_IMMERSIVE_TRANSACTION, data, reply, 0);
boolean res = reply.readInt() == 1;
reply.readException();
data.recycle();
reply.recycle();
return res;
}
public boolean isTopActivityImmersive()
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
mRemote.transact(IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION, data, reply, 0);
boolean res = reply.readInt() == 1;
reply.readException();
data.recycle();
reply.recycle();
return res;
}
private IBinder mRemote;
}

View File

@ -311,6 +311,10 @@ public interface IActivityManager extends IInterface {
public boolean isUserAMonkey() throws RemoteException;
public void finishHeavyWeightApp() throws RemoteException;
public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
public boolean isImmersive(IBinder token) throws RemoteException;
public boolean isTopActivityImmersive() throws RemoteException;
/*
* Private non-Binder interfaces
@ -524,4 +528,7 @@ public interface IActivityManager extends IInterface {
int GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+107;
int FINISH_HEAVY_WEIGHT_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+108;
int HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+109;
int IS_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+110;
int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
}

View File

@ -8725,6 +8725,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
int index = (token != null) ? indexOfTokenLocked(token) : -1;
if (index < 0) {
throw new IllegalArgumentException();
}
HistoryRecord r = (HistoryRecord)mHistory.get(index);
r.immersive = immersive;
}
}
public boolean isImmersive(IBinder token) {
synchronized (this) {
int index = (token != null) ? indexOfTokenLocked(token) : -1;
if (index < 0) {
throw new IllegalArgumentException();
}
HistoryRecord r = (HistoryRecord)mHistory.get(index);
return r.immersive;
}
}
public boolean isTopActivityImmersive() {
synchronized (this) {
HistoryRecord r = topRunningActivityLocked(null);
return (r != null) ? r.immersive : false;
}
}
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready

View File

@ -101,6 +101,7 @@ class HistoryRecord extends IApplicationToken.Stub {
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
boolean immersive; // immersive mode (don't interrupt if possible)
String stringName; // for caching of toString().
@ -153,6 +154,7 @@ class HistoryRecord extends IApplicationToken.Stub {
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
pw.print(" persistent="); pw.print(persistent);
pw.print(" immersive="); pw.print(immersive);
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
pw.print(" visible="); pw.print(visible);
@ -278,6 +280,8 @@ class HistoryRecord extends IApplicationToken.Stub {
} else {
isHomeActivity = false;
}
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
} else {
realActivity = null;
taskAffinity = null;
@ -289,6 +293,7 @@ class HistoryRecord extends IApplicationToken.Stub {
packageName = null;
fullscreen = true;
isHomeActivity = false;
immersive = false;
}
}