Add dispatching overrides for foreground apps.

Apps can register to override the default dispatching
but only when they're in the foreground.

Change-Id: I8e9a9254d3f79f097fb3c8c677d806043574ba4d
This commit is contained in:
Jeff Hamilton
2011-01-08 15:31:26 -06:00
parent 61d9ffbfd8
commit 52d3203ef6
7 changed files with 165 additions and 9 deletions

View File

@ -100406,6 +100406,36 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<method name="disableForegroundDispatch"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="activity" type="android.app.Activity">
</parameter>
</method>
<method name="enableForegroundDispatch"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="activity" type="android.app.Activity">
</parameter>
<parameter name="intent" type="android.app.PendingIntent">
</parameter>
<parameter name="filters" type="android.content.IntentFilter...">
</parameter>
</method>
<method name="getDefaultAdapter" <method name="getDefaultAdapter"
return="android.nfc.NfcAdapter" return="android.nfc.NfcAdapter"
abstract="false" abstract="false"

View File

@ -632,7 +632,7 @@ public class Activity extends ContextThemeWrapper
/*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances; /*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances;
Activity mParent; Activity mParent;
boolean mCalled; boolean mCalled;
private boolean mResumed; /*package*/ boolean mResumed;
private boolean mStopped; private boolean mStopped;
boolean mFinished; boolean mFinished;
boolean mStartedActivity; boolean mStartedActivity;
@ -3827,9 +3827,8 @@ public class Activity extends ContextThemeWrapper
mLastNonConfigurationInstance = null; mLastNonConfigurationInstance = null;
// First call onResume() -before- setting mResumed, so we don't
// send out any status bar / menu notifications the client makes.
mCalled = false; mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this); mInstrumentation.callActivityOnResume(this);
if (!mCalled) { if (!mCalled) {
throw new SuperNotCalledException( throw new SuperNotCalledException(
@ -3838,7 +3837,6 @@ public class Activity extends ContextThemeWrapper
} }
// Now really resume, and install the current status bar and menu. // Now really resume, and install the current status bar and menu.
mResumed = true;
mCalled = false; mCalled = false;
onPostResume(); onPostResume();
if (!mCalled) { if (!mCalled) {
@ -3857,6 +3855,7 @@ public class Activity extends ContextThemeWrapper
"Activity " + mComponent.toShortString() + "Activity " + mComponent.toShortString() +
" did not call through to super.onPause()"); " did not call through to super.onPause()");
} }
mResumed = false;
} }
final void performUserLeaving() { final void performUserLeaving() {
@ -3891,10 +3890,12 @@ public class Activity extends ContextThemeWrapper
mStopped = true; mStopped = true;
} }
mResumed = false;
} }
final boolean isResumed() { /**
* @hide
*/
public final boolean isResumed() {
return mResumed; return mResumed;
} }

View File

@ -185,6 +185,9 @@ public final class ActivityThread {
final HashMap<IBinder, ProviderClientRecord> mLocalProviders final HashMap<IBinder, ProviderClientRecord> mLocalProviders
= new HashMap<IBinder, ProviderClientRecord>(); = new HashMap<IBinder, ProviderClientRecord>();
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
final GcIdler mGcIdler = new GcIdler(); final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false; boolean mGcIdlerScheduled = false;
@ -1424,6 +1427,18 @@ public final class ActivityThread {
} }
} }
public void registerOnActivityPausedListener(Activity activity,
OnActivityPausedListener listener) {
synchronized (mOnPauseListeners) {
ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity);
if (list == null) {
list = new ArrayList<OnActivityPausedListener>();
mOnPauseListeners.put(activity, list);
}
list.add(listener);
}
}
public final ActivityInfo resolveActivityInfo(Intent intent) { public final ActivityInfo resolveActivityInfo(Intent intent) {
ActivityInfo aInfo = intent.resolveActivityInfo( ActivityInfo aInfo = intent.resolveActivityInfo(
mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES);
@ -2333,6 +2348,17 @@ public final class ActivityThread {
} }
} }
r.paused = true; r.paused = true;
// Notify any outstanding on paused listeners
ArrayList<OnActivityPausedListener> listeners;
synchronized (mOnPauseListeners) {
listeners = mOnPauseListeners.remove(r.activity);
}
int size = (listeners != null ? listeners.size() : 0);
for (int i = 0; i < size; i++) {
listeners.get(i).onPaused(r.activity);
}
return state; return state;
} }

View File

@ -1146,6 +1146,7 @@ public class Instrumentation {
* @param activity The activity being resumed. * @param activity The activity being resumed.
*/ */
public void callActivityOnResume(Activity activity) { public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume(); activity.onResume();
if (mActivityMonitors != null) { if (mActivityMonitors != null) {

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2011 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
*/
package android.app;
/**
* A listener that is called when an Activity is paused. Since this is tracked client side
* it should not be trusted to represent the exact current state, but can be used as a hint
* for cleanup.
*
* @hide
*/
public interface OnActivityPausedListener {
/**
* Called when the given activity is paused.
*/
public void onPaused(Activity activity);
}

View File

@ -16,6 +16,9 @@
package android.nfc; package android.nfc;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.nfc.NdefMessage; import android.nfc.NdefMessage;
import android.nfc.Tag; import android.nfc.Tag;
import android.nfc.ILlcpSocket; import android.nfc.ILlcpSocket;
@ -44,6 +47,9 @@ interface INfcAdapter
NdefMessage localGet(); NdefMessage localGet();
void localSet(in NdefMessage message); void localSet(in NdefMessage message);
void openTagConnection(in Tag tag); void openTagConnection(in Tag tag);
void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent,
in IntentFilter[] filters);
void disableForegroundDispatch(in ComponentName activity);
// Non-public methods // Non-public methods
// TODO: check and complete // TODO: check and complete

View File

@ -18,8 +18,12 @@ package android.nfc;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
import android.app.ActivityThread; import android.app.ActivityThread;
import android.app.OnActivityPausedListener;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.IPackageManager; import android.content.pm.IPackageManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.nfc.technology.TagTechnology; import android.nfc.technology.TagTechnology;
@ -211,8 +215,6 @@ public final class NfcAdapter {
private static INfcAdapter sService; private static INfcAdapter sService;
private static INfcTag sTagService; private static INfcTag sTagService;
private final Context mContext;
/** /**
* Helper to check if this device has FEATURE_NFC, but without using * Helper to check if this device has FEATURE_NFC, but without using
* a context. * a context.
@ -308,7 +310,6 @@ public final class NfcAdapter {
if (setupService() == null) { if (setupService() == null) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
mContext = context;
} }
/** /**
@ -410,6 +411,66 @@ public final class NfcAdapter {
} }
} }
class ForegroundDispatchPausedListener implements OnActivityPausedListener {
@Override
public void onPaused(Activity activity) {
disableForegroundDispatchInternal(activity, true);
}
}
/**
* Enables foreground dispatching to the given Activity. This will force all NFC Intents that
* match the given filters to be delivered to the activity bypassing the standard dispatch
* mechanism.
*
* This method must be called from the main thread.
*
* @param activity the Activity to dispatch to
* @param intent the PendingIntent to start for the dispatch
* @param filters the IntentFilters to override dispatching for
* @throws IllegalStateException
*/
public void enableForegroundDispatch(Activity activity, PendingIntent intent,
IntentFilter... filters) {
if (activity == null || intent == null || filters == null) {
throw new NullPointerException();
}
if (!activity.isResumed()) {
throw new IllegalStateException("Foregorund dispatching can onlly be enabled " +
"when your activity is resumed");
}
try {
ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
new ForegroundDispatchPausedListener());
sService.enableForegroundDispatch(activity.getComponentName(), intent, filters);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
}
}
/**
* Disables foreground activity dispatching setup with
* {@link #enableForegroundDispatch}. This must be called before the Activity returns from
* it's <code>onPause()</code> or this method will throw an IllegalStateException.
*
* This method must be called from the main thread.
*/
public void disableForegroundDispatch(Activity activity) {
disableForegroundDispatchInternal(activity, false);
}
void disableForegroundDispatchInternal(Activity activity, boolean force) {
try {
sService.disableForegroundDispatch(activity.getComponentName());
if (!force && !activity.isResumed()) {
throw new IllegalStateException("You must disable forgeground dispatching " +
"while your activity is still resumed");
}
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
}
}
/** /**
* Retrieve a TagTechnology object used to interact with a Tag that is * Retrieve a TagTechnology object used to interact with a Tag that is
* in field. * in field.