跨多个活动的位置服务

时间:2016-06-28 18:49:35

标签: android design-patterns fusedlocationproviderapi

更新 - 编辑代码以显示我的广播提示。使用该方法导致!!!失败的粘合剂交易!并且没有要由toast显示的位置数据。

我想知道是否有一种优雅的方式将谷歌融合位置服务与活动分开。我的警告类目前实现了位置服务,但我有另一个拍摄照片的活动。拍摄照片时,我想将位置与图像相关联。我目前的想法是在我的蓝牙警告班中使用我的广播接收器来收听我的相机将发送的广播并将其与位置相关联。我不是这个想法的粉丝,因为它没有很好地分离功能所以希望提供一些建议或模式。下面的课程代码。

CAMERA CLASS

import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CameraOperations extends AppCompatActivity {
    public final static String ACTION_PICTURE_RECEIVED = "ACTION_PICTURE_RECEIVED";
    public final static String TRANSFER_DATA = "bitmap";
    ImageView mImageView;
    String photoPath;
    int REQUEST_IMAGE_CAPTURE = 1;
    File photoFile = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_view);
        mImageView = (ImageView) findViewById(R.id.imageView);

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            Toast.makeText(CameraOperations.this, "No Camera", Toast.LENGTH_SHORT).show();
        } else {
            dispatchTakePictureIntent();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK){
            if(data == null){
                Toast.makeText(this, "Data is null", Toast.LENGTH_SHORT);
                Bitmap pictureMap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
                mImageView.setImageBitmap(pictureMap);
                broadcastUpdate(ACTION_PICTURE_RECEIVED, pictureMap);
            }else {
                Toast.makeText(this, "Error Occurred", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void broadcastUpdate(final String action, Bitmap picture) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        picture.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        byte[] bytes = stream.toByteArray();

        Intent intent = new Intent(action);
        intent.putExtra(TRANSFER_DATA, bytes);
        sendBroadcast(intent);

        intent = new Intent(CameraOperations.this, Warning.class);
        startActivity(intent);
        finish();
    }

    private File createImageFile() throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(imageFileName, ".jpg", storageDir);

        photoPath = "file:" + image.getAbsolutePath();
        return image;
    }

    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        try{
            photoFile = createImageFile();
        }catch(IOException e){
            e.printStackTrace();
        }

        if(photoFile != null){
            Uri photoUri = Uri.fromFile(photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
        }
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }
}

BLUETOOTH警告类

import android.Manifest;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

public class Warning extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    private final static String TAG = Warning.class.getSimpleName();
    private String mDeviceAddress;
    private HandleConnectionService mBluetoothLeService;
    private boolean quitService;
    private final int PLAY_SERVICES_REQUEST_TIME = 1000;
    private Location mLastLocation;
    private GoogleApiClient mGoogleClient;
    private boolean requestingLocationUpdates = false;
    private LocationRequest locationRequest;

    private int UPDATE_INTERVAL = 10000; // 10 seconds
    private int FASTEST_INTERVAL = 5000; // 5 seconds
    private int DISTANCEMOVED = 10; //In meters


    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((HandleConnectionService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            mBluetoothLeService.connect(mDeviceAddress);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();

            if (HandleConnectionService.ACTION_GATT_DISCONNECTED.equals(action)) {
                if (!quitService) {
                    mBluetoothLeService.connect(mDeviceAddress);
                    Log.w(TAG, "Attempting to reconnect");
                }
                Log.w(TAG, "Disconnected, activity closing");
            } else if (HandleConnectionService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                getGattService(mBluetoothLeService.getSupportedGattService());
            } else if (HandleConnectionService.ACTION_DATA_AVAILABLE.equals(action)) {
                checkWarning(intent.getByteArrayExtra(HandleConnectionService.EXTRA_DATA));
            }else if(CameraOperations.ACTION_PICTURE_RECEIVED.equals(action)){
                byte[] bytes = intent.getByteArrayExtra("bitmap");
                Bitmap picture = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                findLocation();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);

        Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
        setSupportActionBar(myToolbar);

        if (checkGoogleServices()) {
            buildGoogleApiClient();
        }

        quitService = false;
        Intent intent = getIntent();
        mDeviceAddress = intent.getStringExtra(Device.EXTRA_DEVICE_ADDRESS);

        Intent gattServiceIntent = new Intent(this, HandleConnectionService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.mainmenu, menu);
        MenuItem connect = menu.findItem(R.id.connect);
        connect.setVisible(false);
        MenuItem disconnect = menu.findItem(R.id.disconnect);
        disconnect.setVisible(true);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent intent;
        switch (item.getItemId()) {
            case R.id.home:
                new AlertDialog.Builder(this)
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .setTitle("Return Home")
                        .setMessage("Returning home will disconnect you, continue?")
                        .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                quitService = true;
                                mBluetoothLeService.disconnect();
                                mBluetoothLeService.close();

                                Intent intent = new Intent(Warning.this, Main.class);
                                startActivity(intent);
                            }
                        })
                        .setNegativeButton("No", null)
                        .show();

                return true;
            case R.id.connect:
                Toast.makeText(this, "Connect Pressed", Toast.LENGTH_SHORT).show();
                return true;

            case R.id.disconnect:
                disconnectOperation();
                intent = new Intent(Warning.this, Main.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                return true;

            case R.id.profiles:
                Toast.makeText(this, "Profiles Pressed", Toast.LENGTH_SHORT).show();
                return true;


            case R.id.camera:
                Toast.makeText(this, "Camera Pressed", Toast.LENGTH_SHORT).show();
                intent = new Intent(Warning.this, CameraOperations.class);
                startActivity(intent);
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            quitService = true;
            mBluetoothLeService.disconnect();
            mBluetoothLeService.close();

            Intent intent = new Intent(Warning.this, Main.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mBluetoothLeService.disconnect();
        mBluetoothLeService.close();

        System.exit(0);
    }

    public boolean disconnectOperation(){
        this.quitService = true;
        mBluetoothLeService.disconnect();
        mBluetoothLeService.close();
        return true;
    }

    private void checkWarning(byte[] byteArray) {
        if (byteArray != null) {
            for (int i = 0; i < byteArray.length; i++) {
                if (byteArray[i] == 48) {
                   findLocation();
                }
            }
        }
    }

    private void getGattService(BluetoothGattService gattService) {
        if (gattService == null) {
            return;
        }

        BluetoothGattCharacteristic characteristicRx = gattService.getCharacteristic(HandleConnectionService.UUID_BLE_SHIELD_RX);
        mBluetoothLeService.setCharacteristicNotification(characteristicRx, true);
        mBluetoothLeService.readCharacteristic(characteristicRx);
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(HandleConnectionService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(HandleConnectionService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(HandleConnectionService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(HandleConnectionService.ACTION_DATA_AVAILABLE);
        intentFilter.addAction(CameraOperations.ACTION_PICTURE_RECEIVED);

        return intentFilter;
    }

    private boolean checkGoogleServices() {
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        int available = googleAPI.isGooglePlayServicesAvailable(this);
        if (available != ConnectionResult.SUCCESS) {
            if (googleAPI.isUserResolvableError(available)) {
                googleAPI.getErrorDialog(this, available, PLAY_SERVICES_REQUEST_TIME).show();
            }
            return false;
        }

        return true;
    }

    public void findLocation() {
        int REQUEST_CODE_ASK_PERMISSIONS = 123;
        double latitude = 0.0;
        double longitude = 0.0;

        if(Build.VERSION.SDK_INT >= 23){
            boolean fineLocationAccess = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
            boolean courseLocationAccess = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
            boolean accessGranted = fineLocationAccess && courseLocationAccess;

            if (accessGranted) {
                mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleClient);
                if(mLastLocation != null) {
                    latitude = mLastLocation.getLatitude();
                    longitude = mLastLocation.getLongitude();

                    Toast.makeText(this, "latitude: " + latitude + " longitude: " + longitude, Toast.LENGTH_LONG).show();
                }else{
                    Log.i("Warning", "Unable to get Location");
                }
            }else{
                Log.i("Connection", "Request permission");
            }
        }else{
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleClient);
            if(mLastLocation != null) {
                latitude = mLastLocation.getLatitude();
                longitude = mLastLocation.getLongitude();

                Toast.makeText(this, "latitude: " + latitude + " longitude: " + longitude, Toast.LENGTH_LONG).show();
            }else{
                Log.i("Warning", "Unable to get Location");
            }
        }
    }

    public void buildGoogleApiClient() {
        mGoogleClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API).build();
    }

    @Override
    public void onConnected(Bundle bundle) {
        Log.w("Connected", " : " + mGoogleClient.isConnected());
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(this, "Location Services Stopped", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Toast.makeText(this, "Location Services Connection Fail", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (mGoogleClient != null) {
            mGoogleClient.connect();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

我认为只要您以某种方式构建它,您的原始想法实际上就是非常合理的。了解位置信息的活动可以独立于任何其他活动更新和存储该位置。这实际上可能是实际上每隔x个时间运行一次的服务,或者当位置发生变化时。

在同一个类的内部,您可以拥有一个处理该位置信息请求的通用广播接收器。只要以对每个请求者可用的方式返回位置数据,您可以根据需要将其设置为通用,以允许您使用的其他应用程序/活动。

从拍摄照片的活动(以及最终所需的任何活动)中,当您拍照时,您可以发送请求该位置信息的广播,如果没有返回,则发送一些默认或空信息。其中一个复杂因素是知道在完成之前等待这个位置信息需要多长时间。在这种情况下,位置活动是“通用的”,因为如果正确构造位置数据,多个活动可以请求位置数据。

另一种可能强大的方法是使拍照活动成为一般化的。一种方法是,当您拍照时,您可以让您的照片活动向包含照片文件路径/ uri /标识信息的位置活动发送广播,但您不等待响应。相反,您使位置活动侦听此广播,并使用提供的标识信息将位置写入照片元数据以查找所述照片。通过这种方式,您的拍照应用程序不会停留等待位置活动完成(或事件开始,如果它曾经)。

祝你好运,听起来像一个有趣的项目。