com.microsoft.projectoxford.face.samples.ui.IdentificationActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.projectoxford.face.samples.ui.IdentificationActivity.java

Source

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Cognitive Services (formerly Project Oxford): https://www.microsoft.com/cognitive-services
//
// Microsoft Cognitive Services (formerly Project Oxford) GitHub:
// https://github.com/Microsoft/Cognitive-Face-Android
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
package com.microsoft.projectoxford.face.samples.ui;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.google.android.gms.maps.model.TileProvider;
import com.google.firebase.storage.FileDownloadTask;
import com.microsoft.projectoxford.face.FaceServiceClient;
import com.microsoft.projectoxford.face.contract.Face;
import com.microsoft.projectoxford.face.contract.IdentifyResult;
import com.microsoft.projectoxford.face.contract.TrainingStatus;
import com.microsoft.projectoxford.face.samples.R;
import com.microsoft.projectoxford.face.samples.helper.ImageHelper;
import com.microsoft.projectoxford.face.samples.helper.LogHelper;
import com.microsoft.projectoxford.face.samples.helper.SampleApp;
import com.microsoft.projectoxford.face.samples.helper.StorageHelper;
import com.microsoft.projectoxford.face.samples.log.IdentificationLogActivity;
import com.microsoft.projectoxford.face.samples.persongroupmanagement.PersonGroupListActivity;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

// Firebase Example
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.StreamDownloadTask;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.File;

public class IdentificationActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {

    // For TextToSpeech implementation
    public void onInit(int initStatus) {
    }

    // Background task of face identification.
    private class IdentificationTask extends AsyncTask<UUID, String, IdentifyResult[]> {
        private boolean mSucceed = true;
        String mPersonGroupId;

        IdentificationTask(String personGroupId) {
            this.mPersonGroupId = personGroupId;
        }

        @Override
        protected IdentifyResult[] doInBackground(UUID... params) {
            String logString = "Request: Identifying faces ";
            for (UUID faceId : params) {
                logString += faceId.toString() + ", ";
            }
            logString += " in group " + mPersonGroupId;
            addLog(logString);

            // Get an instance of face service client to detect faces in image.
            FaceServiceClient faceServiceClient = SampleApp.getFaceServiceClient();
            try {
                publishProgress("Getting person group status...");

                TrainingStatus trainingStatus = faceServiceClient
                        .getPersonGroupTrainingStatus(this.mPersonGroupId); /* personGroupId */

                if (trainingStatus.status != TrainingStatus.Status.Succeeded) {
                    publishProgress("Person group training status is " + trainingStatus.status);
                    mSucceed = false;
                    return null;
                }

                publishProgress("Identifying...");

                // Start identification.
                return faceServiceClient.identity(this.mPersonGroupId, /* personGroupId */
                        params, /* faceIds */
                        1); /* maxNumOfCandidatesReturned */
            } catch (Exception e) {
                mSucceed = false;
                publishProgress(e.getMessage());
                addLog(e.getMessage());
                return null;
            }
        }

        @Override
        protected void onPreExecute() {
            setUiBeforeBackgroundTask();
        }

        @Override
        protected void onProgressUpdate(String... values) {
            // Show the status of background detection task on screen.a
            setUiDuringBackgroundTask(values[0]);
        }

        @Override
        protected void onPostExecute(IdentifyResult[] result) {
            // Show the result on screen when detection is done.
            setUiAfterIdentification(result, mSucceed);

            Intent checkTTSIntent = new Intent();
            checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
            startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE);

            String words = "David";
            //String words = result[0].faceId.toString();
            //String words = result[0].candidates.get(0).personId.toString();
            //speakWords(words);

            //speak straight away
            myTTS.speak(words, TextToSpeech.QUEUE_FLUSH, null);
        }
    }

    String mPersonGroupId;

    boolean detected;

    FaceListAdapter mFaceListAdapter;

    PersonGroupListAdapter mPersonGroupListAdapter;

    //Set up storage reference called storageRef
    StorageReference storageRef;

    private TextToSpeech myTTS;
    private int MY_DATA_CHECK_CODE = 0;

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

        detected = false;

        progressDialog = new ProgressDialog(this);
        progressDialog.setTitle(getString(R.string.progress_dialog_title));

        LogHelper.clearIdentificationLog();

        // My Code
        //        storageRef = FirebaseStorage.getInstance().getReference();
        //        storageRef = storageRef.child("david5.JPG");
        //
        //        File localFile = null;
        //
        //        //Download file from firebase
        //        try{
        //            localFile = File.createTempFile("firebase_example", "JPG");
        //            FileDownloadTask fileDownloadTask = storageRef.getFile(localFile);
        //            OnSuccessListener successListener = new OnSuccessListener() {
        //                @Override
        //                public void onSuccess(Object o) {
        //                    Log.d("download", "success!");
        //                }
        //            };
        //            OnFailureListener failureListener = new OnFailureListener() {
        //                @Override
        //                public void onFailure(@NonNull Exception e) {
        //                    Log.d("download", "failure!");
        //                }
        //            };
        //            fileDownloadTask.addOnSuccessListener(successListener);
        //            fileDownloadTask.addOnFailureListener(failureListener);
        //
        //        }
        //
        //        catch(Exception e){
        //            Log.e("download",e.getMessage());
        //
        //        }

        // Start a background task to download image.
        new DownloadTask("").execute();

        progressDialog = new ProgressDialog(this);
        progressDialog.setTitle("Please Wait");
    }

    // Background task of face identification.
    private class DownloadTask extends AsyncTask<String, String, String> {

        DownloadTask(String personGroupId) {
        };

        @Override
        protected String doInBackground(String... values) {
            try {
                //String src = storageRef.getDownloadUrl().toString();
                String src = "https://firebasestorage.googleapis.com/v0/b/resight-d6b9c.appspot.com/o/david5.JPG?alt=media&token=111b57fb-f49a-4ab6-aa68-4a55f9eb4bee";
                java.net.URL url = new java.net.URL(src);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoInput(true);
                connection.connect();
                InputStream input = connection.getInputStream();
                Bitmap myBitmap = BitmapFactory.decodeStream(input);
                mBitmap = myBitmap;

            } catch (Exception e) {
                e.printStackTrace();
            }

            return "";
        }

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected void onProgressUpdate(String... values) {
        }

        @Override
        protected void onPostExecute(String values) {
            Uri imageUri = Uri.parse("android.resource://" + getPackageName() + "/drawable/david1");
            Intent intentImageData = new Intent();
            intentImageData.setData(imageUri);

            onActivityResult(0, -1, intentImageData);

        }
    }

    protected void onPostExecute(Face[] result) {
        this.identify(new View(this));
    }

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

        ListView listView = (ListView) findViewById(R.id.list_person_groups_identify);
        mPersonGroupListAdapter = new PersonGroupListAdapter();
        listView.setAdapter(mPersonGroupListAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                setPersonGroupSelected(position);
            }
        });

        if (mPersonGroupListAdapter.personGroupIdList.size() != 0) {
            setPersonGroupSelected(0);
        } else {
            setPersonGroupSelected(-1);
        }
    }

    void setPersonGroupSelected(int position) {
        TextView textView = (TextView) findViewById(R.id.text_person_group_selected);
        if (position > 0) {
            String personGroupIdSelected = mPersonGroupListAdapter.personGroupIdList.get(position);
            mPersonGroupListAdapter.personGroupIdList.set(position,
                    mPersonGroupListAdapter.personGroupIdList.get(0));
            mPersonGroupListAdapter.personGroupIdList.set(0, personGroupIdSelected);
            ListView listView = (ListView) findViewById(R.id.list_person_groups_identify);
            listView.setAdapter(mPersonGroupListAdapter);
            setPersonGroupSelected(0);
        } else if (position < 0) {
            setIdentifyButtonEnabledStatus(false);
            textView.setTextColor(Color.RED);
            textView.setText(R.string.no_person_group_selected_for_identification_warning);
        } else {
            mPersonGroupId = mPersonGroupListAdapter.personGroupIdList.get(0);
            String personGroupName = StorageHelper.getPersonGroupName(mPersonGroupId, IdentificationActivity.this);
            refreshIdentifyButtonEnabledStatus();
            textView.setTextColor(Color.BLACK);
            textView.setText(String.format("Person group to use: %s", personGroupName));
        }
    }

    private void setUiBeforeBackgroundTask() {
        progressDialog.show();
    }

    // Show the status of background detection task on screen.
    private void setUiDuringBackgroundTask(String progress) {
        progressDialog.setMessage(progress);

        setInfo(progress);
    }

    // Show the result on screen when detection is done.
    private void setUiAfterIdentification(IdentifyResult[] result, boolean succeed) {
        progressDialog.dismiss();

        setAllButtonsEnabledStatus(true);
        setIdentifyButtonEnabledStatus(false);

        if (succeed) {
            // Set the information about the detection result.
            setInfo("Identification is done");

            if (result != null) {
                mFaceListAdapter.setIdentificationResult(result);

                String logString = "Response: Success. ";
                for (IdentifyResult identifyResult : result) {
                    logString += "Face " + identifyResult.faceId.toString() + " is identified as "
                            + (identifyResult.candidates.size() > 0
                                    ? identifyResult.candidates.get(0).personId.toString()
                                    : "Unknown Person")
                            + ". ";
                }
                addLog(logString);

                // Show the detailed list of detected faces.
                ListView listView = (ListView) findViewById(R.id.list_identified_faces);
                listView.setAdapter(mFaceListAdapter);
            }
        }
    }

    // Background task of face detection.
    private class DetectionTask extends AsyncTask<InputStream, String, Face[]> {
        @Override
        protected Face[] doInBackground(InputStream... params) {
            // Get an instance of face service client to detect faces in image.
            FaceServiceClient faceServiceClient = SampleApp.getFaceServiceClient();
            try {
                publishProgress("Detecting...");

                // Start detection.
                return faceServiceClient.detect(params[0], /* Input stream of image to detect */
                        true, /* Whether to return face ID */
                        false, /* Whether to return face landmarks */
                        /* Which face attributes to analyze, currently we support:
                           age,gender,headPose,smile,facialHair */
                        null);
            } catch (Exception e) {
                publishProgress(e.getMessage());
                return null;
            }
        }

        @Override
        protected void onPreExecute() {
            setUiBeforeBackgroundTask();
        }

        @Override
        protected void onProgressUpdate(String... values) {
            // Show the status of background detection task on screen.
            setUiDuringBackgroundTask(values[0]);
        }

        @Override
        protected void onPostExecute(Face[] result) {
            progressDialog.dismiss();

            setAllButtonsEnabledStatus(true);

            if (result != null) {
                // Set the adapter of the ListView which contains the details of detected faces.
                mFaceListAdapter = new FaceListAdapter(result);
                ListView listView = (ListView) findViewById(R.id.list_identified_faces);
                listView.setAdapter(mFaceListAdapter);

                if (result.length == 0) {
                    detected = false;
                    setInfo("No faces detected!");
                } else {
                    detected = true;
                    setInfo("Click on the \"Identify\" button to identify the faces in image.");
                }
            } else {
                detected = false;
            }

            refreshIdentifyButtonEnabledStatus();
            identify(mFaceListAdapter);
        }

        // Called when the "Detect" button is clicked.
        protected void identify(FaceListAdapter mFaceListAdapter) {
            // Start detection task only if the image to detect is selected.
            if (detected && mPersonGroupId != null) {
                // Start a background task to identify faces in the image.
                List<UUID> faceIds = new ArrayList<>();
                for (Face face : mFaceListAdapter.faces) {
                    faceIds.add(face.faceId);
                }

                setAllButtonsEnabledStatus(false);

                new IdentificationTask(mPersonGroupId).execute(faceIds.toArray(new UUID[faceIds.size()]));
            } else {
                // Not detected or person group exists.
                setInfo("Please select an image and create a person group first.");
            }
        }
    }

    // Flag to indicate which task is to be performed.
    private static final int REQUEST_SELECT_IMAGE = 0;

    // The image selected to detect.
    private Bitmap mBitmap;

    // Progress dialog popped up when communicating with server.
    ProgressDialog progressDialog;

    // Called when image selection is done.
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == MY_DATA_CHECK_CODE) {
            myTTS = new TextToSpeech(this, this);
        }

        switch (requestCode) {
        case REQUEST_SELECT_IMAGE:
            if (resultCode == RESULT_OK) {
                detected = false;

                // If image is selected successfully, set the image URI and bitmap.
                Uri imageUri = data.getData();
                if (mBitmap == null) {
                    mBitmap = ImageHelper.loadSizeLimitedBitmapFromUri(imageUri, getContentResolver());
                    if (mBitmap != null) {
                        // Show the image on screen.
                        ImageView imageView = (ImageView) findViewById(R.id.image);
                        imageView.setImageBitmap(mBitmap);
                    }
                }

                // Clear the identification result.
                FaceListAdapter faceListAdapter = new FaceListAdapter(null);
                ListView listView = (ListView) findViewById(R.id.list_identified_faces);
                listView.setAdapter(faceListAdapter);

                // Clear the information panel.
                setInfo("");

                // TODO -> Load image from Firebase, and save it as a Bitmap Object here.

                // Start detecting in image.
                detect(mBitmap);
            }
            break;
        default:
            break;
        }
    }

    // Start detecting in image.
    private void detect(Bitmap bitmap) {
        // Put the image into an input stream for detection.
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(output.toByteArray());

        setAllButtonsEnabledStatus(false);

        // Start a background task to detect faces in the image.
        new DetectionTask().execute(inputStream);
    }

    // Called when the "Select Image" button is clicked.
    public void selectImage(View view) {
        Intent intent = new Intent(this, SelectImageActivity.class);
        startActivityForResult(intent, REQUEST_SELECT_IMAGE);
    }

    // Called when the "Detect" button is clicked.
    public void identify(View view) {
        // Start detection task only if the image to detect is selected.
        if (detected && mPersonGroupId != null) {
            // Start a background task to identify faces in the image.
            List<UUID> faceIds = new ArrayList<>();
            for (Face face : mFaceListAdapter.faces) {
                faceIds.add(face.faceId);
            }

            setAllButtonsEnabledStatus(false);

            new IdentificationTask(mPersonGroupId).execute(faceIds.toArray(new UUID[faceIds.size()]));
        } else {
            // Not detected or person group exists.
            setInfo("Please select an image and create a person group first.");
        }
    }

    public void managePersonGroups(View view) {
        Intent intent = new Intent(this, PersonGroupListActivity.class);
        startActivity(intent);

        refreshIdentifyButtonEnabledStatus();
    }

    public void viewLog(View view) {
        Intent intent = new Intent(this, IdentificationLogActivity.class);
        startActivity(intent);
    }

    // Add a log item.
    private void addLog(String log) {
        LogHelper.addIdentificationLog(log);
    }

    // Set whether the buttons are enabled.
    private void setAllButtonsEnabledStatus(boolean isEnabled) {
        Button selectImageButton = (Button) findViewById(R.id.manage_person_groups);
        selectImageButton.setEnabled(isEnabled);

        Button groupButton = (Button) findViewById(R.id.select_image);
        groupButton.setEnabled(isEnabled);

        Button identifyButton = (Button) findViewById(R.id.identify);
        identifyButton.setEnabled(isEnabled);

        Button viewLogButton = (Button) findViewById(R.id.view_log);
        viewLogButton.setEnabled(isEnabled);
    }

    // Set the group button is enabled or not.
    private void setIdentifyButtonEnabledStatus(boolean isEnabled) {
        Button button = (Button) findViewById(R.id.identify);
        button.setEnabled(isEnabled);
    }

    // Set the group button is enabled or not.
    private void refreshIdentifyButtonEnabledStatus() {
        if (detected && mPersonGroupId != null) {
            setIdentifyButtonEnabledStatus(true);
        } else {
            setIdentifyButtonEnabledStatus(false);
        }
    }

    // Set the information panel on screen.
    private void setInfo(String info) {
        TextView textView = (TextView) findViewById(R.id.info);
        textView.setText(info);
    }

    // The adapter of the GridView which contains the details of the detected faces.
    private class FaceListAdapter extends BaseAdapter {
        // The detected faces.
        List<Face> faces;

        List<IdentifyResult> mIdentifyResults;

        // The thumbnails of detected faces.
        List<Bitmap> faceThumbnails;

        // Initialize with detection result.
        FaceListAdapter(Face[] detectionResult) {
            faces = new ArrayList<>();
            faceThumbnails = new ArrayList<>();
            mIdentifyResults = new ArrayList<>();

            if (detectionResult != null) {
                faces = Arrays.asList(detectionResult);
                for (Face face : faces) {
                    try {
                        // Crop face thumbnail with five main landmarks drawn from original image.
                        faceThumbnails.add(ImageHelper.generateFaceThumbnail(mBitmap, face.faceRectangle));
                    } catch (IOException e) {
                        // Show the exception when generating face thumbnail fails.
                        setInfo(e.getMessage());
                    }
                }
            }
        }

        public void setIdentificationResult(IdentifyResult[] identifyResults) {
            mIdentifyResults = Arrays.asList(identifyResults);
        }

        @Override
        public boolean isEnabled(int position) {
            return false;
        }

        @Override
        public int getCount() {
            return faces.size();
        }

        @Override
        public Object getItem(int position) {
            return faces.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = layoutInflater.inflate(R.layout.item_face_with_description, parent, false);
            }
            convertView.setId(position);

            // Show the face thumbnail.
            ((ImageView) convertView.findViewById(R.id.face_thumbnail))
                    .setImageBitmap(faceThumbnails.get(position));

            if (mIdentifyResults.size() == faces.size()) {
                // Show the face details.
                DecimalFormat formatter = new DecimalFormat("#0.00");
                if (mIdentifyResults.get(position).candidates.size() > 0) {
                    String personId = mIdentifyResults.get(position).candidates.get(0).personId.toString();
                    String personName = StorageHelper.getPersonName(personId, mPersonGroupId,
                            IdentificationActivity.this);
                    String identity = "Person: " + personName + "\n" + "Confidence: "
                            + formatter.format(mIdentifyResults.get(position).candidates.get(0).confidence);
                    ((TextView) convertView.findViewById(R.id.text_detected_face)).setText(identity);
                } else {
                    ((TextView) convertView.findViewById(R.id.text_detected_face))
                            .setText(R.string.face_cannot_be_identified);
                }
            }

            return convertView;
        }
    }

    // The adapter of the ListView which contains the person groups.
    private class PersonGroupListAdapter extends BaseAdapter {
        List<String> personGroupIdList;

        // Initialize with detection result.
        PersonGroupListAdapter() {
            personGroupIdList = new ArrayList<>();

            Set<String> personGroupIds = StorageHelper.getAllPersonGroupIds(IdentificationActivity.this);

            for (String personGroupId : personGroupIds) {
                personGroupIdList.add(personGroupId);
                if (mPersonGroupId != null && personGroupId.equals(mPersonGroupId)) {
                    personGroupIdList.set(personGroupIdList.size() - 1,
                            mPersonGroupListAdapter.personGroupIdList.get(0));
                    mPersonGroupListAdapter.personGroupIdList.set(0, personGroupId);
                }
            }
        }

        @Override
        public int getCount() {
            return personGroupIdList.size();
        }

        @Override
        public Object getItem(int position) {
            return personGroupIdList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = layoutInflater.inflate(R.layout.item_person_group, parent, false);
            }
            convertView.setId(position);

            // set the text of the item
            String personGroupName = StorageHelper.getPersonGroupName(personGroupIdList.get(position),
                    IdentificationActivity.this);
            int personNumberInGroup = StorageHelper
                    .getAllPersonIds(personGroupIdList.get(position), IdentificationActivity.this).size();
            ((TextView) convertView.findViewById(R.id.text_person_group))
                    .setText(String.format("%s (Person count: %d)", personGroupName, personNumberInGroup));

            if (position == 0) {
                ((TextView) convertView.findViewById(R.id.text_person_group))
                        .setTextColor(Color.parseColor("#3399FF"));
            }

            return convertView;
        }
    }

    /**
     * Service to handle downloading files from Firebase Storage.
     */
    public class MyDownloadService {

        private static final String TAG = "Storage#DownloadService";

        /** Actions **/
        public static final String ACTION_DOWNLOAD = "action_download";
        public static final String DOWNLOAD_COMPLETED = "download_completed";
        public static final String DOWNLOAD_ERROR = "download_error";

        /** Extras **/
        public static final String EXTRA_DOWNLOAD_PATH = "extra_download_path";
        public static final String EXTRA_BYTES_DOWNLOADED = "extra_bytes_downloaded";

        private StorageReference mStorageRef;

        public void onCreate() {
            // Initialize Storage
            mStorageRef = FirebaseStorage.getInstance().getReference();
        }

        @Nullable
        public IBinder onBind(Intent intent) {
            return null;
        }

        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "onStartCommand:" + intent + ":" + startId);

            if (ACTION_DOWNLOAD.equals(intent.getAction())) {
                // Get the path to download from the intent
                String downloadPath = intent.getStringExtra(EXTRA_DOWNLOAD_PATH);
                downloadFromPath(downloadPath);
            }

            return 0;
        }

        private void downloadFromPath(final String downloadPath) {
            Log.d(TAG, "downloadFromPath:" + downloadPath);

            // Mark task started
            //taskStarted();
            //showProgressNotification(getString(R.string.progress_downloading), 0, 0);

            // Download and get total bytes
            mStorageRef.child(downloadPath).getStream(new StreamDownloadTask.StreamProcessor() {
                @Override
                public void doInBackground(StreamDownloadTask.TaskSnapshot taskSnapshot, InputStream inputStream)
                        throws IOException {
                    long totalBytes = taskSnapshot.getTotalByteCount();
                    long bytesDownloaded = 0;

                    byte[] buffer = new byte[1024];
                    int size;

                    while ((size = inputStream.read(buffer)) != -1) {
                        bytesDownloaded += size;
                        //showProgressNotification(getString(R.string.progress_downloading),
                        //        bytesDownloaded, totalBytes);
                    }

                    // Close the stream at the end of the Task
                    inputStream.close();
                }
            }).addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) {
                    Log.d(TAG, "download:SUCCESS");

                    // Send success broadcast with number of bytes downloaded
                    broadcastDownloadFinished(downloadPath, taskSnapshot.getTotalByteCount());
                    showDownloadFinishedNotification(downloadPath, (int) taskSnapshot.getTotalByteCount());

                    // Mark task completed
                    //taskCompleted();
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception exception) {
                    Log.w(TAG, "download:FAILURE", exception);

                    // Send failure broadcast
                    broadcastDownloadFinished(downloadPath, -1);
                    showDownloadFinishedNotification(downloadPath, -1);

                    // Mark task completed
                    //taskCompleted();
                }
            });
        }

        /**
         * Broadcast finished download (success or failure).
         * @return true if a running receiver received the broadcast.
         */
        private boolean broadcastDownloadFinished(String downloadPath, long bytesDownloaded) {
            boolean success = bytesDownloaded != -1;
            String action = success ? DOWNLOAD_COMPLETED : DOWNLOAD_ERROR;

            Intent broadcast = new Intent(action).putExtra(EXTRA_DOWNLOAD_PATH, downloadPath)
                    .putExtra(EXTRA_BYTES_DOWNLOADED, bytesDownloaded);
            return LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(broadcast);
        }

        /**
         * Show a notification for a finished download.
         */
        private void showDownloadFinishedNotification(String downloadPath, int bytesDownloaded) {
            // Hide the progress notification
            //dismissProgressNotification();

            // Make Intent to MainActivity
            Intent intent = new Intent().putExtra(EXTRA_DOWNLOAD_PATH, downloadPath)
                    .putExtra(EXTRA_BYTES_DOWNLOADED, bytesDownloaded)
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

            boolean success = bytesDownloaded != -1;
            String caption = success ? "Success!" : "Failure";
            //showFinishedNotification(caption, intent, true);
        }

        public IntentFilter getIntentFilter() {
            IntentFilter filter = new IntentFilter();
            filter.addAction(DOWNLOAD_COMPLETED);
            filter.addAction(DOWNLOAD_ERROR);

            return filter;
        }
    }
}