How to use the camera Flash light as torch? - android

The following is my code to enable Flashlight in Android. In a Toast message it returned the following parameters: auto, on, off, torch. After setting the parameters to FLASH_MODE_TORCH, the torch is not enabling for me. My Manifest permissions are:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<permission android:name="android.permission.FLASHLIGHT"
android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
android:protectionLevel="normal"/>
<uses-feature android:name="android.hardware.camera"></uses-feature>
My Code:
public class CameraFlashActivity extends Activity {
/** Called when the activity is first created. */
public Camera mCamera;
Parameters cameraParameters;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
boolean cameraPresnt = this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
if( cameraPresnt == true)
{
Toast.makeText(getApplicationContext(),"Flash is there",10).show();
if(mCamera==null)
{
try {
mCamera = Camera.open();
mCamera.startPreview();
Toast.makeText(getApplicationContext(),"Camera is present",10).show();
} catch (Exception e) {
Log.i("CameraFlashActivity", "Camera.open() failed: " + e.getMessage());
}
if(mCamera==null)
{
Toast.makeText(getApplicationContext(),"Camera object is null",10).show();
}
else
{
cameraParameters = mCamera.getParameters();
if(cameraParameters==null)
{
Toast.makeText(getApplicationContext(),"No camera parameters",10).show();
}
else
{
List<String> flashmodes = cameraParameters.getSupportedFlashModes();
String supportedFlashModes = cameraParameters.getFlashMode();
Toast.makeText(getApplicationContext(),flashmodes.toString(),10).show();
Toast.makeText(getApplicationContext(),supportedFlashModes,10).show();
cameraParameters.setFlashMode(Parameters.FLASH_MODE_ON);
mCamera.setParameters(cameraParameters);
String supportFlashModes = cameraParameters.getFlashMode();
Toast.makeText(getApplicationContext(),supportFlashModes,10).show();
Toast.makeText(getApplicationContext(),"Camera parameters are set on flash light",10).show();
}
}
}
else
{
}
}
else
{
Toast.makeText(getApplicationContext(),"Flash is not there",10).show();
}
}
public void onDestroy() {
super.onDestroy();
if (mCamera != null) {
cameraParameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(cameraParameters);
mCamera.stopPreview();
mCamera.release();
}
Log.i("cameraFlashLight", "onDestroy");
}
}

Use this sequence of code:
mCamera = Camera.open();
cameraParameters = mCamera.getParameters();
cameraParameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
mCamera.startPreview();

Related

Android:How to make sure that Camera is released propelry, when I am using AsyncTask to open it?

I am using AsyncTask to open camera in background thread. It works fine but at times(10 % chance) it will not release the camera and I will have blank preview screen if I open my app again and native camera shouts at me saying you haven't released camera resource.
Below is my AsyncTask code.
private class AsyncCamera extends AsyncTask<Void, Void, Camera> {
int _id = 0;
public AsyncCamera(int id) {
_id = id;
}
protected void onPreExecute() {
progressBar.setVisibility(View.VISIBLE);
releaseCameraAndPreview();
}
protected Camera doInBackground(Void... params) {
Camera camera;
camera = getCamera(_id);
return camera;
}
protected void onPostExecute(Camera camera) {
mCamera = camera;
setCamera(camera);
safeToTakePicture = true;
progressBar.setVisibility(View.GONE);
}
}
And releaseCameraAndPreview() method
private void releaseCameraAndPreview() {
setCamera(null);//to stop preview and clear frame layout
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
here getCamera() method
private Camera getCamera(int id) {
try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
} catch (Exception e) {
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}
return mCamera;
}
And last setCamera() method.
public void setCamera(Camera camera) {
if (camera == null) {
if (mCamera != null) {
mCamera.stopPreview();
}
return;
} else {
//set parameters.clear frame layout and add view to it and start preview
mCamera = camera;
params = mCamera.getParameters();
setPictureAndPreviewSize();
if (mCamera != null) {
mPreview = new Preview(getApplicationContext(), mCamera);
mPreviewLayout.removeAllViews();
mPreviewLayout.addView(mPreview);
}
}
}
onPause() method
#Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mPreview.getHolder().removeCallback(mPreview);
mCamera.release();
mCamera = null;
}
}
I can produce the same error by pressing lock screen button while my app is open.

Camera.takePicture() gives no callback

SOLVED, SEE COMMENT ---
I never get a callback from Camera.takePicture(), I see that in logcat.
What is missing? How do I make takePicture()... take a picture?!
Most of this is directly from Android developers camera guide. I want to take pictures programmatically without any preview or user action. Using the built in camera app works fine. SDK 16.
And in the manifest I do have added:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera"
android:required="true" />
The code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bTake = (Button)findViewById(R.id.b_take);
boolean boo = safeCameraOpen(camId);
Camera.Parameters parameters = mCamera.getParameters();
mCamera.setParameters(parameters);
bTake.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mCamera.takePicture(shutter, null, null, picture);
Log.e(getString(R.string.app_name), "After takePicture");
}
});
}// END onCreate
/* Camera operations */
private ShutterCallback shutter = new ShutterCallback() {
#Override
public void onShutter() {
Log.e(getString(R.string.app_name), "onShutter");
}
};
private PictureCallback picture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] arg0, Camera arg1) {
Log.e(getString(R.string.app_name), "onPicTaken");
}
};
/* Starting up and closing down*/
private boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
releaseCamera();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}
return qOpened;
}
private void releaseCamera() {
if (mCamera != null) {
((Camera) mCamera).release();
mCamera = null;
Log.e(getString(R.string.app_name), "cam released");
}
}}
Try:
mCamera.takePicture(shutter, picture, picture);

getting null pointer exception on camera set parameters

I'm getting an error of a NullPointerException and it is referencing the line Camera.Parameters:
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w,
int h) {
// TODO Auto-generated method stub
Log.e(TAG, "surfaceChanged");
if (mPreviewRunning) {
mCamera.stopPreview();
mPreviewRunning = false;
Log.e(TAG, "stopPeview");
}
Camera.Parameters p = mCamera.getParameters();
Log.e(TAG, "paarameters");
p.setPreviewSize(w, h);
mCamera.setParameters(p);
Log.e(TAG, " set parameters");
try {
mCamera.setPreviewDisplay(holder);
Log.e(TAG, "setPreview");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
Could anyone tell me what the error is in the above code.
I will have to test it again to get the log, but here is the entire code. I did alter the manifest.
public class Picture extends Activity implements SurfaceHolder.Callback{
private String TAG;
private LayoutInflater mInflater = null;
private Camera mCamera;
boolean mPreviewRunning = false;
private SurfaceHolder mSurfaceHolder;
private SurfaceView mSurfaceView;
Button takepicture;
byte[] tempdata;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.camera_surface);
mCamera = getCameraInstance ();
mSurfaceView = (SurfaceView)findViewById(R.id.surface_camera);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mInflater = LayoutInflater.from(this);
View overView = mInflater.inflate(R.layout.cameraoverlay, null);
this.addContentView(overView, new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
takepicture = (Button) findViewById(R.id.button);
takepicture.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
mCamera.takePicture(shutterCallback, mPictureCallback,
jpegCallback);
}
});
}
private Camera getCameraInstance() {
// TODO Auto-generated method stub
Camera mCamera = null;
try {
mCamera = Camera.open();
} catch (Exception e) {
}
return mCamera;
}
ShutterCallback shutterCallback = new ShutterCallback() {
#Override
public void onShutter() {}
};
PictureCallback mPictureCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera c) {}
};
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera c) {
if(data != null) {
tempdata=data;
done();
}
}
};
void done(){
Bitmap bm = BitmapFactory.decodeByteArray(tempdata, 0, tempdata.length);
String url = Images.Media.insertImage(getContentResolver(),
bm, null, null);
bm.recycle();
Bundle bundle = new Bundle();
if(url!=null) {
bundle.putString("url", url);
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
} else {
Toast.makeText(this, "Picture can not be saved",
Toast.LENGTH_SHORT).show();
}
finish();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w,
int h) {
// TODO Auto-generated method stub
Log.e(TAG, "surfaceChanged");
if (mPreviewRunning) {
mCamera.stopPreview();
mPreviewRunning = false;
Log.e(TAG, "stopPeview");
}
Camera.Parameters p = mCamera.getParameters();
Log.e(TAG, "paarameters");
p.setPreviewSize(w, h);
mCamera.setParameters(p);
Log.e(TAG, " set parameters");
try {
mCamera.setPreviewDisplay(holder);
Log.e(TAG, "setPreview");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e(TAG, "surfaceCreated");
try {
mCamera = Camera.open();
Log.e(TAG, "camera open");
} catch(Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e(TAG, "surfaceDestroyed");
mCamera.stopPreview();
mPreviewRunning = false;
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
Also, the line the log referenced was the line: Camera.Parameters p = mCamera.getParameters();
Looks like mCamera is null... did you initialize it? (mCamera = Camera.open();)
See the docs for a checklist as it pertains to taking pictures
Add the below three lines in manifeast file::
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
To take pictures with this class, use the following steps:
1) Obtain an instance of Camera from open(int).
2) Get existing (default) settings with getParameters().
3) If necessary, modify the returned Camera.Parameters object and call
setParameters(Camera.Parameters).
4) If desired, call setDisplayOrientation(int).
5) Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder). Without a surface, the camera will be unable to start the preview.
6) Important: Call startPreview() to start updating the preview surface. Preview must be started before you can take a picture.
7) When you want, call takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback) to capture a photo. Wait for the callbacks to provide the actual image data.
8) After taking a picture, preview display will have stopped. To take more photos, call startPreview() again first.
9) Call stopPreview() to stop updating the preview surface.
10) Important: Call release() to release the camera for use by other applications. Applications should release the camera immediately in onPause() (and re-open() it in onResume()).
There are many solutions, but this is an easy, dead cheap option that worked for me:
try{
mCamera.autoFocus(autoFocusCB); //Or whatever part of code that crashes
}
catch(Exception e){
Log.v("joshtag","THIS PHONE DOES NOT SUPPORT AUTOFOCUS!!"); //a warning, popup, whatever
}
Voilà! Trap deactivated.
Of course, dont forget the manifest permissions....

How to turn on the Android Flashlight

Update
Check out my answer
Original
I'm trying to turn on the camera flashlight on the LG Revolution within my program. I use the torch mode method which works on most phones but not on LG phone. Does anyone know how to get it to work on LG's or specifically the Revolution?
Here's my manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.FLASHLIGHT"/>
Here's my current code:
public Camera camera = Camera.open();
public Camera.Parameters Flash = camera.getParameters();
With my on create:
Flash.setFlashMode("torch");
Parameters p = camera.getParameters();
camera.setParameters(Flash);
camera.startPreview();
I've seen people use an auto focus but i don't know if that would work.
I thought I would update this with some bullet prof code that works on almost all 4.0+ devices.
public void turnOn() {
camera = Camera.open();
try {
Parameters parameters = camera.getParameters();
parameters.setFlashMode(getFlashOnParameter());
camera.setParameters(parameters);
camera.setPreviewTexture(new SurfaceTexture(0));
camera.startPreview();
camera.autoFocus(this);
} catch (Exception e) {
// We are expecting this to happen on devices that don't support autofocus.
}
}
private String getFlashOnParameter() {
List<String> flashModes = camera.getParameters().getSupportedFlashModes();
if (flashModes.contains(FLASH_MODE_TORCH)) {
return FLASH_MODE_TORCH;
} else if (flashModes.contains(FLASH_MODE_ON)) {
return FLASH_MODE_ON;
} else if (flashModes.contains(FLASH_MODE_AUTO)) {
return FLASH_MODE_AUTO;
}
throw new RuntimeException();
}
The real key is setting that fake SurfaceTexture so that the preview will actually start. Turning it off is very easy as well
public void turnOff() {
try {
camera.stopPreview();
camera.release();
camera = null;
} catch (Exception e) {
// This will happen if the camera fails to turn on.
}
}
It seems like the developer of the Tiny Flashlight + LED app on the Android Market figured out how to make the flashlight work on LG Revolution.
Maybe you can contact him and ask?
You can also check the permissions he is using in his app to try to make your app work!
Good luck!
Test this :
if(camera == null){
camera = Camera.open();
parameters = camera.getParameters();
List<String> flashModes = parameters.getSupportedFlashModes();
if(flashModes != null && flashModes.contains(Parameters.FLASH_MODE_TORCH)){
//appareil supportant le mode torch
parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
} else if (flashModes != null && flashModes.contains(Parameters.FLASH_MODE_ON)){
//spécial samsung
parameters.setFlashMode(Parameters.FLASH_MODE_ON);
camera.setParameters(parameters);
camera.startPreview();
camera.autoFocus(new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) { }
});
} else {
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
}
parameters.setFlashMode( Parameters.FLASH_MODE_OFF );
camera.setParameters(parameters);
camera.release();
camera = null;
} catch (RuntimeException e) {}
}//if
This worked well for LG Nexus:
camera = Camera.open();
camera.setPreviewTexture(new SurfaceTexture(0));
camera.setParameters(p);
camera.startPreview();
/*TESTED LG G4 */
public void flashOnOff(){
List<String> flashModes = parameter001.getSupportedFlashModes();
if(flashModes != null && flashModes.contains(Parameters.FLASH_MODE_TORCH)){
//appareil supportant le mode torch
parameter001.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameter001);
} else if (flashModes != null && flashModes.contains(Parameters.FLASH_MODE_ON)){
//spécial samsung
parameter001.setFlashMode(Parameters.FLASH_MODE_ON);
mCamera.setParameters(parameter001);
mCamera.autoFocus(new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) { }
});
} else {
parameter001.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameter001);
}
if (!isFlashOn) {
if (mCamera == null || parameter001 == null) {
return;
}
parameter001 = mCamera.getParameters();
parameter001.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameter001);
try {
mCamera.setPreviewTexture(new SurfaceTexture(0));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
isFlashOn = true;
// changing button/switch image
}else if (isFlashOn) {
if (mCamera == null || parameter001 == null) {
return;
}
parameter001 = mCamera.getParameters();
parameter001.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameter001);
mCamera.stopPreview();
isFlashOn = false;
}
}

switch back/front camera on fly

If I use only CAMERA_FACING_BACK or CAMERA_FACING_FRONT all works fine.
I have trouble with switch from CAMERA_FACING_BACK to CAMERA_FACING_FRONT.
My code snippet:
public class PhotoCameraActivity extends Activity implements OnClickListener {
private SurfaceView cameraView;
private Button turnButton;
private Camera camera = null;
private Callback listener;
private static int camId = Camera.CameraInfo.CAMERA_FACING_BACK;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photo_camera_main);
prepareActivity();
}
private void prepareActivity() {
cameraView = (SurfaceView) findViewById(R.id.photo_camera_surface_view);
turnButton = (ImageButton) findViewById(R.id.turn_button);
turnButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v.equals(turnButton)) {
if (Camera.getNumberOfCameras() > 1 && camId < Camera.getNumberOfCameras() - 1) {
startCamera(camId + 1);
} else {
startCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
}
}
}
#Override
protected void onResume() {
startCamera(camId);
super.onResume();
}
#Override
protected void onPause() {
stopCamera();
super.onPause();
}
private void startCamera(int cameraId) {
if (camera != null) {
stopCamera();
}
holder = cameraView.getHolder();
listener = new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
};
holder.addCallback(listener);
camId = cameraId;
camera = Camera.open(cameraId);
Camera.Parameters params = camera.getParameters();
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
params.setPreviewSize(1280, 800);
} else {
params.setPreviewSize(640, 480);
}
camera.setParameters(params);
}
private void stopCamera(){
System.out.println("stopCamera method");
if (camera != null){
camera.stopPreview();
camera.setPreviewCallback(null);
camera.release();
camera = null;
holder.removeCallback(listener);
holder = null;
}
}
}
In onCreate() of my activity I add the following onClick listener to a button overlayed on my Preview SurfaceView (there are numerous example on the web for previewing):
ImageButton useOtherCamera = (ImageButton) findViewById(R.id.useOtherCamera);
//if phone has only one camera, hide "switch camera" button
if(Camera.getNumberOfCameras() == 1){
useOtherCamera.setVisibility(View.INVISIBLE);
}
else {
useOtherCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (inPreview) {
camera.stopPreview();
}
//NB: if you don't release the current camera before switching, you app will crash
camera.release();
//swap the id of the camera to be used
if(currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
else {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
camera = Camera.open(currentCameraId);
//Code snippet for this method from somewhere on android developers, i forget where
setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
try {
//this step is critical or preview on new camera will no know where to render to
camera.setPreviewDisplay(previewHolder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
On my test device the back camera has an ID of 0 and the front has an id of 1. I suggest using Camera.CameraInfo static variables for your camera id's rather than hard-coding values. I am sure that will only cause issues on other devices.
I restart Activity with cameraId = 2 and this is working.
I removed and re-added SurfaceView in my layout and so it worked.
public void switchCamera(int cameraType)
{
if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT))
{
if(Camera.getNumberOfCameras() > cameraType)
{
// Set selected camera
this.cameraType = cameraType;
}
else
{
// Set default camera (Rear)
this.cameraType = Camera.CameraInfo.CAMERA_FACING_BACK;
}
if(mCamera != null)
{
// Destroy previuos Holder
surfaceDestroyed(holder);
holder.removeCallback(this);
// Remove and re-Add SurfaceView
ViewGroup rootLayout = (ViewGroup) surface.getParent();
rootLayout.removeView(surface);
surface = new SurfaceView(context);
holder = surface.getHolder();
holder.addCallback(this);
rootLayout.addView(surface, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
}
I have to remove the surface preview before adding a new one:
if (mPreview != null) {
mPreview.surfaceDestroyed(mPreview.getHolder());
mPreview.getHolder().removeCallback(mPreview);
mPreview.destroyDrawingCache();
FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_frame);
preview.removeView(mPreview);
mPreview.mCamera = null;
mPreview = null;
}
//then add your preview
Use this code:
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//swap the id of the camera to be used
if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
else
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
try {
mCamera = Camera.open(currentCameraId);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
}
catch (Exception e) { e.printStackTrace(); }
try using this:
camera.stopPreview();
if(camera != null)
camera.release();
try {
camera = Camera.open(/*new id*/);
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception ex) { ex.printStackTrace(); }

Categories

Resources