Dennis van Opstal Dennis van Opstal - 14 days ago 10
Android Question

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>

Answer

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.

Comments