Search code examples
javaandroidbroadcastreceiverandroid-camera

CONNECTIVITY_CHANGE called when opening Camera


I'm trying to build an application with a camera that does something when the connectivity changes from 3G/4G or something else to Wi-Fi. To detect when this happens, I'm using a class that extends BroadCastReceiver:

public class WifiReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
        boolean syncPopup = sharedPref.getBoolean("syncPopup", true);
        if (syncPopup) {
            DigginSQLiteHelper db = new DigginSQLiteHelper(context);
            if (db.getAllToBeSyncedPhotos().size() > 0) {
                ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo networkInfo = null;
                if (manager != null) {
                    networkInfo = manager.getActiveNetworkInfo();
                }
                SharedPreferences sharedPref2 = context.getSharedPreferences(context.getString(R.string.connType), Context.MODE_PRIVATE);
                int connType = sharedPref2.getInt(context.getString(R.string.connType), 0);
                if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnectedOrConnecting() && connType != ConnectivityManager.TYPE_WIFI) {
                    showNotification(context);
                }
                SharedPreferences.Editor editor = sharedPref2.edit();
                editor.putInt(context.getString(R.string.connType), networkInfo.getType());
                editor.commit();
            }
        } else {
            ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo;
            if (manager != null) {
                networkInfo = manager.getActiveNetworkInfo();
                SharedPreferences.Editor editor= context.getSharedPreferences(context.getString(R.string.connType), Context.MODE_PRIVATE).edit();
                editor.putInt(context.getString(R.string.connType), networkInfo.getType());
                editor.commit();
            }
        }
    }

    private void showNotification(Context context) {
        PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
                new Intent(context, MainActivity.class), 0);

        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(context)
                        .setSmallIcon(R.drawable.icon_camera)
                        .setContentTitle("My notification")
                        .setContentText("Connected to Wifi, ready to sync!");
        mBuilder.setContentIntent(contentIntent);
        mBuilder.setDefaults(Notification.DEFAULT_SOUND);
        mBuilder.setAutoCancel(true);
        NotificationManager mNotificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(1, mBuilder.build());

    }
}

But whenever the Activity with my Camera opens and closes the WifiReceiver detects it as a change (this is not what I want, I only want this to happen when the user connects through WiFi). I just don't know what to check or what else to do so that showNotification(Context context) isn't called when I open/close the CameraActivity.

Here is the CameraActity:

package com.example.sition.diggin;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;

import com.example.sition.diggin.compass.BearingToNorthProvider;
import com.example.sition.diggin.misc.CameraView;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

@SuppressWarnings("deprecation")
public class CameraActivity extends AppCompatActivity implements BearingToNorthProvider.ChangeEventListener {

    //region fields
    private Camera mCamera;
    private CameraView mCameraView;
    private float mDist = 0f;
    private String flashMode;
    private ImageButton flashButton;
    private Intent intent;
    private BearingToNorthProvider mBearingProvider;
    private boolean pictureTaken = false;
    private byte[] currentData;

    private double bearing;
    private double currentBearing = 0d;
    private String cardinalDirection = "?";
    private double rotation = 0d;

    private final int REQUEST_CODE_ASK_PERMISSIONS = 2;
    //endregion

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        mCamera = getCameraInstance();
        mCameraView = new CameraView(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mCameraView);
        ImageButton captureButton = (ImageButton) findViewById(R.id.btnCapture);
        captureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Camera.Parameters params = mCamera.getParameters();
                params.setFlashMode(flashMode);
                mCamera.setParameters(params);
                mCamera.takePicture(null, null, mPicture);
            }
        });
        SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
        flashMode = sharedPref.getString(getString(R.string.flashMode), Camera.Parameters.FLASH_MODE_OFF);
        flashButton = (ImageButton) findViewById(R.id.btnFlash);
        setFlashButton();
        mBearingProvider = new BearingToNorthProvider(this,this);
        mBearingProvider.setChangeEventListener(this);
        mBearingProvider.start();
    }

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

    /**
     * Helper method to access the camera returns null if it cannot get the
     * camera or does not exist
     *
     * @return the instance of the camera
     */
    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            Log.e("CamException", e.toString());
        }
        return camera;
    }

    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            currentData = data;
            mBearingProvider.updateBearing();
            bearing = mBearingProvider.getBearing();
            cardinalDirection = bearingToString(bearing);
            Log.e("Direction", cardinalDirection + "," + bearing);
            findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
            findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
            findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
            findViewById(R.id.ivFocusPoint).setVisibility(View.INVISIBLE);
            findViewById(R.id.ivCompassPointer).setVisibility(View.INVISIBLE);
            findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
        }
    };

    //region Picture Intents
    /**
     * Method that puts the necessary data in the intent and then sends it back to the ProjectOverview
     * @param v the view that activated this method
     */
    public void confirmPicture(View v) {
        String path = createFile(currentData);
        intent = new Intent();
        intent.putExtra("path", path);
        String description = String.valueOf(((EditText) findViewById(R.id.tvPictureDesc)).getText());
        intent.putExtra("direction", cardinalDirection);
        intent.putExtra("description", description);

        //close this Activity...
        setResult(Activity.RESULT_OK, intent);
        finish();
    }

    /**
     * Method that puts no data in the intent and then sends it back to the ProjectOverview
     * @param v the view that activated this method
     */
    public void deletePicture(View v) {
        setResult(Activity.RESULT_CANCELED);
        finish();
    }

    /**
     * Method that restarts the camera giving the user a change to retake the picture
     * @param v the view that activated this method
     */
    public void retryPicture(View v) {
        findViewById(R.id.btnFlash).setVisibility(View.VISIBLE);
        findViewById(R.id.btnCapture).setVisibility(View.VISIBLE);
        findViewById(R.id.ivCompass).setVisibility(View.VISIBLE);
        findViewById(R.id.ivFocusPoint).setVisibility(View.VISIBLE);
        findViewById(R.id.ivCompassPointer).setVisibility(View.VISIBLE);
        findViewById(R.id.pictureOverlay).setVisibility(View.INVISIBLE);
        pictureTaken = false;
        mCamera.startPreview();
    }

    //endregion

    //region File Methods

    /**
     * Method that creates a file from the given byte array and saves the file in the Pictures Directory
     * @param data is the array of bytes that represent the picture taken by the camera
     * @return the path of created file
     */
    private String createFile(byte[] data){
        checkFilePermissions();
        File picFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "tempPic.jpg" + File.separator);
        String path = picFile.getPath();
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(picFile));
            bos.write(data);
            bos.flush();
            bos.close();
            return path;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * Checks the permission for reading to and writing from the external storage
     */
    private void checkFilePermissions() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            int hasWriteExternalStoragePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        REQUEST_CODE_ASK_PERMISSIONS);
            }
        }
    }

    //endregion

    //region Zoom Methods

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Get the pointer ID
        Camera.Parameters params = mCamera.getParameters();
        int action = event.getAction();


        if (event.getPointerCount() > 1) {
            // handle multi-touch events
            if (action == MotionEvent.ACTION_POINTER_DOWN) {
                mDist = getFingerSpacing(event);
            } else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
                mCamera.cancelAutoFocus();
                handleZoom(event, params);
            }
        } else {
            // handle single touch events
            if (action == MotionEvent.ACTION_UP) {
                handleFocus(event, params);
            }
        }
        return true;
    }

    /**
     * Method that handles the zoom of the camera
     * @param event the event that activated this method
     * @param params the parameters of the camera
     */
    private void handleZoom(MotionEvent event, Camera.Parameters params) {
        int maxZoom = params.getMaxZoom();
        int zoom = params.getZoom();
        float newDist = getFingerSpacing(event);
        if (newDist > mDist) {
            //zoom in
            if (zoom < maxZoom)
                zoom++;
        } else if (newDist < mDist) {
            //zoom out
            if (zoom > 0)
                zoom--;
        }
        mDist = newDist;
        params.setZoom(zoom);
        mCamera.setParameters(params);
    }

    /**
     * Method that handles the focus of the camera when zooming
     * @param event the event that activated this method
     * @param params the parameters of the camera
     */
    private void handleFocus(MotionEvent event, Camera.Parameters params) {
        int pointerId = event.getPointerId(0);
        int pointerIndex = event.findPointerIndex(pointerId);
        // Get the pointer's current position

        List<String> supportedFocusModes = params.getSupportedFocusModes();
        if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            mCamera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean b, Camera camera) {
                    // currently set to auto-focus on single touch
                }
            });
        }
    }

    /** Determine the space between the first two fingers */
    private float getFingerSpacing(MotionEvent event) {
        double x = event.getX(0) - event.getX(1);
        double y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    //endregion

    //region Flash Methods

    /**
     * Method that changes the flash mode the camera currently uses (on/off/auto)
     * @param v the view that activated this method
     */
    public void changeFlashMode(View v) {
        switch (flashMode) {
            case Camera.Parameters.FLASH_MODE_ON :
                flashMode = Camera.Parameters.FLASH_MODE_AUTO;
                break;
            case Camera.Parameters.FLASH_MODE_AUTO :
                flashMode = Camera.Parameters.FLASH_MODE_OFF;
                break;
            case Camera.Parameters.FLASH_MODE_OFF :
                flashMode = Camera.Parameters.FLASH_MODE_ON;
                break;
        }
        SharedPreferences sharedPref = getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putString(getString(R.string.flashMode), flashMode);
        editor.commit();
        setFlashButton();
    }

    /**
     * Method that changes the image of the button based on flash mode
     */
    private void setFlashButton() {
        switch (flashMode) {
            case Camera.Parameters.FLASH_MODE_ON :
                flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_on));
                break;
            case Camera.Parameters.FLASH_MODE_AUTO :
                flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_auto));
                break;
            case Camera.Parameters.FLASH_MODE_OFF :
                flashButton.setImageDrawable(getResources().getDrawable(R.drawable.camera_flash_off));
                break;
        }
    }

    //endregion

    //region Bearing Methods

    /**
     * Method that gives a cardinal direction based on the current bearing to the true north
     * @param bearing is the bearing to the true north
     * @return cardinal direction that belongs to the bearing
     */
    private String bearingToString(Double bearing) {
        String strHeading = "?";
        if (isBetween(bearing,-180.0,-157.5)) { strHeading = "South"; }
        else if (isBetween(bearing,-157.5,-112.5)) { strHeading = "SouthWest"; }
        else if (isBetween(bearing,-112.5,-67.5)) { strHeading = "West"; }
        else if (isBetween(bearing,-67.5,-22.5)) { strHeading = "NorthWest"; }
        else if (isBetween(bearing,-22.5,22.5)) { strHeading = "North"; }
        else if (isBetween(bearing,22.5,67.5)) { strHeading = "NorthEast"; }
        else if (isBetween(bearing,67.5,112.5)) { strHeading = "East"; }
        else if (isBetween(bearing,112.5,157.5)) { strHeading = "SouthEast"; }
        else if (isBetween(bearing,157.5,180.0)) { strHeading = "South"; }
        return strHeading;
    }

    /**
     * Method that checks if a certain number is in a certain range of numbers
     * @param x is the number to check
     * @param lower is the number that defines the lower boundary of the number range
     * @param upper is the number that defines the upper boundary of the number range
     * @return true if the number is between the other numbers, false otherwise
     */
    private boolean isBetween(double x, double lower, double upper) {
        return lower <= x && x <= upper;
    }

    /*
    Method that triggers when the bearing changes, it sets the current bearing and sends an updated context to the provider
     */
    @Override
    public void onBearingChanged(double bearing) {
        this.bearing = bearing;
        mBearingProvider.setContext(this);

        ImageView image = (ImageView) findViewById(R.id.ivCompass);
        if (image.getVisibility() == View.VISIBLE) {

            // create a rotation animation (reverse turn degree degrees)
            if (bearing < 0) {
                bearing += 360;
            }
            RotateAnimation ra = new RotateAnimation((float) currentBearing, (float) -bearing, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

            // how long the animation will take place
            ra.setDuration(210);

            // set the animation after the end of the reservation status
            ra.setFillAfter(true);

            // Start the animation
            image.startAnimation(ra);
            rotation += currentBearing + bearing;
            currentBearing = -bearing;
        } else if (!pictureTaken){

            ImageView image2 = (ImageView) findViewById(R.id.ivCompass2);
            image2.setRotation((float) -rotation);
            image.clearAnimation();
            pictureTaken = true;
        }
        mBearingProvider.setContext(this);
    }

    //endregion
}

EDIT:

To make things worse, the WifiReceiver seems to call onReceive at other places in the application too (seemingly at random).

EDIT 2:

I don't know if this helps but here is a piece of code from the manifest regarding the WifiReceiver:

<receiver android:name=".misc.WifiReceiver">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

Solution

  • After searching for an answer for quite some time I stumbled upon this post. And I found out that I was using the wrong action in my intent-filter, so now I'm using:

    <receiver android:name=".misc.WifiReceiver">
        <intent-filter>
            <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/>
            <action android:name="android.net.wifi.STATE_CHANGE"/>
        </intent-filter>
    </receiver>
    

    I haven't gotten the chance to test it in the exact situation I would like it to work, but for now it seems to work pretty well.