简体   繁体   中英

Update UI using IntentService with ActivityRecognition

I am using the new APIs for locations and trying the ActivityRecognition. I used the code base that its on the Google Developers Training and everything went just fine until I wanted to update the UI. (I'm aware that you cannot update the UI directly on a service and that's why I'm using a handler/interface/something.) What I tried:

Using an interface, but this was useless since I didn't find a way to get the Activity context from the IntentService.

Also tried passing a handler to the IntentService for it to send a message with the action but for some reason whenever it reaches the call to this:

 // If the intent contains an update
    if (ActivityRecognitionResult.hasResult(intent)) {

this just returns false since I'm passing the messenger with the handler. If I remove the line that add the extra containing the message it works just fine but I cannot update my UI.

I tried removing the extra before starting the service but no success. Any Ideas how should I fix this?

These are the codes I have:

Activity:

public class MainActivity extends FragmentActivity implements ConnectionCallbacks, OnConnectionFailedListener {

public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 20;
public static final int DETECTION_INTERVAL_MILLISECONDS = MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;

private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;
private Context mContext;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private static final String TAG = "MainActivity";
public static final String KEY_ACTION = "user_action";

public enum REQUEST_TYPE {START, STOP}
private REQUEST_TYPE mRequestType;
private TextView tvAction;


//FIXME I'm aware of this potential leak
Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        Bundle reply = msg.getData();
        final String userAction = reply.getString(KEY_ACTION);
        tvAction.setText("You are now: "+userAction);
        // do whatever with the bundle here
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mContext = this;

    tvAction = (TextView)findViewById(R.id.tv_action);
    // Start with the request flag set to false
    mInProgress = false;

    mActivityRecognitionClient =
        new ActivityRecognitionClient(mContext, this, this);

    Intent intent = new Intent(
            mContext, ActivityRecognitionIntentService.class);
            //putting this will make return false.
    intent.putExtra("messenger", new Messenger(handler)); 


    mActivityRecognitionPendingIntent =
        PendingIntent.getService(mContext, 0, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);
}

private void startUpdates() {

    mRequestType = REQUEST_TYPE.START;

    // Check for Google Play services

    if (!servicesConnected()) {
        return;
    }
    // If a request is not already underway
    if (!mInProgress) {
        // Indicate that a request is in progress
        mInProgress = true;
        // Request a connection to Location Services
        mActivityRecognitionClient.connect();
    //
    }
}


private void stopUpdates() {
    // Set the request type to STOP
    mRequestType = REQUEST_TYPE.STOP;
    /*
     * Test for Google Play services after setting the request type.
     * If Google Play services isn't present, the request can be
     * restarted.
     */
    if (!servicesConnected()) {
        return;
    }
    // If a request is not already underway
    if (!mInProgress) {
        // Indicate that a request is in progress
        mInProgress = true;
        // Request a connection to Location Services
        mActivityRecognitionClient.connect();
    //
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Decide what to do based on the original request code
    switch (requestCode) {
    case CONNECTION_FAILURE_RESOLUTION_REQUEST:
        /*
         * If the result code is Activity.RESULT_OK, try to connect again
         */
        switch (resultCode) {
        case Activity.RESULT_OK:
            /*
             * Try the request again
             */
            break;
        }

    }
}

private boolean servicesConnected() {
    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {
        // In debug mode, log the status
        Log.d("Activity Recognition", "Google Play services is available.");
        // Continue
        return true;
        // Google Play services was not available for some reason
    } else {
        // Get the error dialog from Google Play services
        Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                CONNECTION_FAILURE_RESOLUTION_REQUEST);

        // If Google Play services can provide an error dialog
        if (errorDialog != null) {
            // Create a new DialogFragment for the error dialog
            ErrorDialogFragment errorFragment = new ErrorDialogFragment();
            // Set the dialog in the DialogFragment
            errorFragment.setDialog(errorDialog);
            // Show the error dialog in the DialogFragment
            errorFragment.show(getSupportFragmentManager(), "Activity Recognition");
        }
        return false;
    }
}

// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
    // Global field to contain the error dialog
    private Dialog mDialog;

    // Default constructor. Sets the dialog field to null
    public ErrorDialogFragment() {
        super();
        mDialog = null;
    }

    // Set the dialog to display
    public void setDialog(Dialog dialog) {
        mDialog = dialog;
    }

    // Return a Dialog to the DialogFragment.
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return mDialog;
    }
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    // Turn off the request flag
    mInProgress = false;
    /*
     * If the error has a resolution, start a Google Play services
     * activity to resolve it.
     */
    if (connectionResult.hasResolution()) {
        try {
            connectionResult.startResolutionForResult(
                    this,
                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
        } catch (SendIntentException e) {
            // Log the error
            e.printStackTrace();
        }
    // If no resolution is available, display an error dialog
    } else {
        // Get the error code
        int errorCode = connectionResult.getErrorCode();
        // Get the error dialog from Google Play services
        Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
                errorCode,
                this,
                CONNECTION_FAILURE_RESOLUTION_REQUEST);
        // If Google Play services can provide an error dialog
        if (errorDialog != null) {
            // Create a new DialogFragment for the error dialog
            ErrorDialogFragment errorFragment =
                    new ErrorDialogFragment();
            // Set the dialog in the DialogFragment
            errorFragment.setDialog(errorDialog);
            // Show the error dialog in the DialogFragment
            errorFragment.show(
                    getSupportFragmentManager(),
                    "Activity Recognition");
        }
    }
}

@Override
public void onConnected(Bundle arg0) {


    switch (mRequestType) {
    case START:
        /*
     * Request activity recognition updates using the preset
     * detection interval and PendingIntent. This call is
     * synchronous.
     */
    mActivityRecognitionClient.requestActivityUpdates(
            DETECTION_INTERVAL_MILLISECONDS,
            mActivityRecognitionPendingIntent);
        break;
    case STOP :
        mActivityRecognitionClient.removeActivityUpdates(
                mActivityRecognitionPendingIntent);
        break;

    default :
        Log.e(TAG, "Unknown request type in onConnected().");
        break;
    }


    /*
     * Since the preceding call is synchronous, turn off the
     * in progress flag and disconnect the client
     */
    mInProgress = false;
    mActivityRecognitionClient.disconnect();
}

@Override
public void onDisconnected() {
    // Turn off the request flag
    mInProgress = false;
    // Delete the client
    mActivityRecognitionClient = null;
}

@Override
protected void onPause() {
    super.onPause();
    stopUpdates();
}

@Override
protected void onResume() {
    super.onResume();
    startUpdates();
}

@Override
protected void onStop() {
    super.onStop();
    stopUpdates();
}


}

IntentService

import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;

public class ActivityRecognitionIntentService extends IntentService {

    // Formats the timestamp in the log
    private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss.SSSZ";

    private static final String TAG = "ActivityRecognitionIntentService";

    private static final String SHARED_PREFERENCES = "ActivityRecognition";

    private static final String KEY_PREVIOUS_ACTIVITY_TYPE = "KEY_PREVIOUS_ACTIVITY_TYPE";

    // A date formatter
    private SimpleDateFormat mDateFormat;

    // Store the app's shared preferences repository
    private SharedPreferences mPrefs;
    protected Messenger messenger;

    public ActivityRecognitionIntentService() {
        // Set the label for the service's background thread
        super("ActivityRecognitionIntentService");

    }

    public ActivityRecognitionIntentService(String name) {
        super(name);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Bundle extras = intent.getExtras();
        messenger = (Messenger) extras.get("messenger");
        intent.removeExtra("messenger");
        super.onStart(intent, startId);
    }

    /**
     * Called when a new activity detection update is available.
     */
    @Override
    protected void onHandleIntent(Intent intent) {

        // Get a handle to the repository
        Context mContext = getApplicationContext();


        mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);

        // Get a date formatter, and catch errors in the returned timestamp
        try {
            mDateFormat = (SimpleDateFormat) DateFormat.getDateTimeInstance();
        } catch (Exception e) {
            Log.e(TAG, getString(R.string.date_format_error));
        }

        // Format the timestamp according to the pattern, then localize the
        // pattern
        mDateFormat.applyPattern(DATE_FORMAT_PATTERN);
        mDateFormat.applyLocalizedPattern(mDateFormat.toLocalizedPattern());

        // If the intent contains an update
        if (ActivityRecognitionResult.hasResult(intent)) {

            // Get the update
            ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);


            // Get the most probable activity from the list of activities in the
            // update
            DetectedActivity mostProbableActivity = result.getMostProbableActivity();

            // Get the confidence percentage for the most probable activity
            int confidence = mostProbableActivity.getConfidence();

            // Get the type of activity
            int activityType = mostProbableActivity.getType();

            // Check to see if the repository contains a previous activity
            if (!mPrefs.contains(KEY_PREVIOUS_ACTIVITY_TYPE)) {

                // This is the first type an activity has been detected. Store
                // the type
                Editor editor = mPrefs.edit();
                editor.putInt(KEY_PREVIOUS_ACTIVITY_TYPE, activityType);
                editor.commit();

                // If the repository contains a type
            } else if (
                // The confidence level for the current activity is > 50%
                    (confidence >= 50)) {

                // Notify the user
                final String userAction = getNameFromType(activityType);
                sendNotification(userAction);

                if (messenger != null) {
                    Message msg = Message.obtain();
                    Bundle data = new Bundle();
                        data.putString(MainActivity.KEY_ACTION, userAction);
                    msg.setData(data); //put the data here
                    try {
                        messenger.send(msg);
                    } catch (RemoteException e) {
                            Log.i("error", "error");
                    }
                }
            }
        }
    }

    /**
     * Post a notification to the user. The notification prompts the user to
     * click it to open the device's GPS settings
     */
    private void sendNotification(String type) {

        // Create a notification builder that's compatible with platforms >=
        // version 4
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());

        // Set the title, text, and icon
        builder.setContentTitle(getString(R.string.app_name)).setContentText(type)// getString(R.string.turn_on_GPS))
                .setSmallIcon(R.drawable.ic_launcher)

                // Get the Intent that starts the Location settings panel
                .setContentIntent(getContentIntent());

        // Get an instance of the Notification Manager
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // Build the notification and post it
        notifyManager.notify(0, builder.build());
    }

    /**
     * Get a content Intent for the notification
     * 
     * @return A PendingIntent that starts the device's Location Settings panel.
     */
    private PendingIntent getContentIntent() {

        // Set the Intent action to open Location Settings
        Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);

        // Create a PendingIntent to start an Activity
        return PendingIntent.getActivity(getApplicationContext(), 0, gpsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    }


    /**
     * Map detected activity types to strings
     * 
     * @param activityType
     *            The detected activity type
     * @return A user-readable name for the type
     */
    private String getNameFromType(int activityType) {
        switch (activityType) {
        case DetectedActivity.IN_VEHICLE:
            return "in_vehicle";
        case DetectedActivity.ON_BICYCLE:
            return "on_bicycle";
        case DetectedActivity.ON_FOOT:
            return "on_foot";
        case DetectedActivity.STILL:
            return "still";
        case DetectedActivity.UNKNOWN:
            return "unknown";
        case DetectedActivity.TILTING:
            return "tilting";
        }
        return "unknown";
    }
}

IntentServices are not allowed to update UI, since they are designed to run background tasks.

You should design your service interface based on a notification instead, that when clicked can open an app activity to show detailed info about the service status.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM