org.odk.collect.android.fragments.ShowQRCodeFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.odk.collect.android.fragments.ShowQRCodeFragment.java

Source

/* Copyright (C) 2017 Shobhit
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.odk.collect.android.fragments;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;

import org.json.JSONException;
import org.json.JSONObject;
import org.odk.collect.android.R;
import org.odk.collect.android.activities.MainMenuActivity;
import org.odk.collect.android.activities.ScannerWithFlashlightActivity;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.listeners.QRCodeListener;
import org.odk.collect.android.preferences.AdminPreferencesActivity;
import org.odk.collect.android.utilities.CompressionUtils;
import org.odk.collect.android.utilities.LocaleHelper;
import org.odk.collect.android.utilities.SharedPreferencesUtils;
import org.odk.collect.android.utilities.ToastUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.zip.DataFormatException;

import timber.log.Timber;

import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.odk.collect.android.preferences.AdminKeys.KEY_ADMIN_PW;
import static org.odk.collect.android.preferences.PreferenceKeys.KEY_PASSWORD;
import static org.odk.collect.android.utilities.QRCodeUtils.decodeFromBitmap;
import static org.odk.collect.android.utilities.QRCodeUtils.generateQRBitMap;
import static org.odk.collect.android.utilities.QRCodeUtils.saveBitmapToCache;

public class ShowQRCodeFragment extends Fragment implements View.OnClickListener, QRCodeListener {

    private static final int SELECT_PHOTO = 111;
    private boolean[] checkedItems = new boolean[] { true, true };
    private ImageView qrImageView;
    private ProgressBar progressBar;
    private Intent shareIntent;
    private TextView editQRCode;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.show_qrcode_fragment, container, false);
        Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
        toolbar.setTitle(getString(R.string.import_export_settings));
        ((AdminPreferencesActivity) getActivity()).setSupportActionBar(toolbar);
        setHasOptionsMenu(true);
        setRetainInstance(true);
        qrImageView = (ImageView) view.findViewById(R.id.qr_iv);
        progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
        editQRCode = (TextView) view.findViewById(R.id.edit_qrcode);
        editQRCode.setOnClickListener(this);
        Button scan = (Button) view.findViewById(R.id.btnScan);
        scan.setOnClickListener(this);
        Button select = (Button) view.findViewById(R.id.btnSelect);
        select.setOnClickListener(this);
        generateCode();
        return view;
    }

    public void generateCode() {
        addPasswordStatusString();
        new GenerateQRCode(this).execute();
    }

    private void addPasswordStatusString() {
        String status;
        if (checkedItems[0] && checkedItems[1]) {
            status = getString(R.string.qrcode_with_both_passwords);
        } else if (checkedItems[0]) {
            status = getString(R.string.qrcode_with_admin_password);
        } else if (checkedItems[1]) {
            status = getString(R.string.qrcode_with_server_password);
        } else {
            status = getString(R.string.qrcode_without_passwords);
        }
        editQRCode.setText(status);
    }

    private void updateShareIntent(Bitmap qrCode) throws IOException {

        // Send an intent to share saved image
        shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.setType("image/*");

        // Save the bitmap to a file
        File shareFile = saveBitmapToCache(qrCode);
        shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + shareFile));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btnScan:
            IntentIntegrator integrator = IntentIntegrator.forFragment(this);
            integrator.setCaptureActivity(ScannerWithFlashlightActivity.class).setBeepEnabled(true)
                    .setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES).setOrientationLocked(false)
                    .setPrompt(getString(R.string.qrcode_scanner_prompt)).initiateScan();
            break;

        case R.id.btnSelect:
            Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
            photoPickerIntent.setType("image/*");
            startActivityForResult(photoPickerIntent, SELECT_PHOTO);
            break;

        case R.id.edit_qrcode:
            String[] items = new String[] { getString(R.string.admin_password),
                    getString(R.string.server_password) };

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                    .setTitle(R.string.include_password_dialog)
                    .setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                            checkedItems[which] = isChecked;
                        }
                    }).setCancelable(false)
                    .setPositiveButton(R.string.generate, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            generateCode();
                            dialog.dismiss();
                        }
                    }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            AlertDialog dialog = builder.create();
            dialog.show();
            break;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);

        if (result != null) {
            if (result.getContents() == null) {
                // request was canceled...
                Timber.i("QR code scanning cancelled");
            } else {
                applySettings(result.getContents());
                return;
            }
        }

        if (requestCode == SELECT_PHOTO) {
            if (resultCode == Activity.RESULT_OK) {
                try {
                    final Uri imageUri = data.getData();
                    final InputStream imageStream = getActivity().getContentResolver().openInputStream(imageUri);

                    final Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
                    String response = decodeFromBitmap(bitmap);
                    if (response != null) {
                        applySettings(response);
                    }
                } catch (FileNotFoundException e) {
                    Timber.e(e);
                }
            } else {
                Timber.i("Choosing QR code from sdcard cancelled");
            }
        }
    }

    private void applySettings(String content) {
        String decompressedData;
        try {
            decompressedData = CompressionUtils.decompress(content);
            JSONObject jsonObject = new JSONObject(decompressedData);
            SharedPreferencesUtils prefUtils = new SharedPreferencesUtils();
            prefUtils.savePreferencesFromJSON(jsonObject);
        } catch (DataFormatException e) {
            Timber.e(e);
            ToastUtils.showShortToast(getString(R.string.invalid_qrcode));
            return;
        } catch (IOException | JSONException e) {
            Timber.e(e);
            return;
        }

        getActivity().finish();
        final LocaleHelper localeHelper = new LocaleHelper();
        localeHelper.updateLocale(getActivity());
        Intent intent = new Intent(getActivity().getBaseContext(), MainMenuActivity.class);
        getActivity().startActivity(intent);
        getActivity().overridePendingTransition(0, 0);
        getActivity().finishAffinity();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.settings_menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_item_share:
            getActivity().startActivity(Intent.createChooser(shareIntent, getString(R.string.share_qrcode)));
            return true;
        case R.id.menu_save_preferences:
            File writeDir = new File(Collect.SETTINGS);
            if (!writeDir.exists()) {
                if (!writeDir.mkdirs()) {
                    ToastUtils.showShortToast("Error creating directory " + writeDir.getAbsolutePath());
                    return false;
                }
            }

            File dst = new File(writeDir.getAbsolutePath() + "/collect.settings");
            boolean success = AdminPreferencesActivity.saveSharedPreferencesToFile(dst, getActivity());
            if (success) {
                ToastUtils.showLongToast("Settings successfully written to " + dst.getAbsolutePath());
            } else {
                ToastUtils.showLongToast("Error writing settings to " + dst.getAbsolutePath());
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void preExecute() {
        progressBar.setVisibility(VISIBLE);
        qrImageView.setVisibility(GONE);
    }

    @Override
    public void bitmapGenerated(Bitmap bitmap) {
        progressBar.setVisibility(GONE);
        qrImageView.setVisibility(VISIBLE);

        if (bitmap != null) {
            qrImageView.setImageBitmap(bitmap);
            try {
                updateShareIntent(bitmap);
            } catch (IOException e) {
                Timber.e(e);
            }
        }
    }

    private Collection<String> getSelectedPasswordKeys() {
        Collection<String> keys = new ArrayList<>();

        //adding the selected password keys
        if (checkedItems[0]) {
            keys.add(KEY_ADMIN_PW);
        }

        if (checkedItems[1]) {
            keys.add(KEY_PASSWORD);
        }
        return keys;
    }

    private class GenerateQRCode extends AsyncTask<Void, Void, Bitmap> {
        private final QRCodeListener listener;

        GenerateQRCode(QRCodeListener listener) {
            this.listener = listener;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            listener.preExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            listener.bitmapGenerated(bitmap);
        }

        @Override
        protected Bitmap doInBackground(Void... params) {
            return generateQRBitMap(getSelectedPasswordKeys());
        }
    }
}