Java tutorial
/* BalticApp, for studying and tracking the condition of the Baltic sea and Gulf of Finland throug user submissions. Copyright (C) 2016 Daniel Zakharin, LuKe This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/> or the beginning of MainActivity.java file. */ package com.luke.lukef.lukeapp.fragments; import android.app.Dialog; import android.app.Fragment; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.location.Location; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.Nullable; import android.support.v4.content.FileProvider; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import com.luke.lukef.lukeapp.Constants; import com.luke.lukef.lukeapp.MainActivity; import com.luke.lukef.lukeapp.R; import com.luke.lukef.lukeapp.tools.SubmissionFetchService; import com.luke.lukef.lukeapp.model.Category; import com.luke.lukef.lukeapp.model.Submission; import com.luke.lukef.lukeapp.popups.CategoriesPopup; import com.luke.lukef.lukeapp.tools.LukeNetUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.concurrent.ExecutionException; import static android.app.Activity.RESULT_OK; /** * Handles the new submission screen, includes sending the submissions, dispatching the camera intent * and listening to the category pop up clicks. */ public class NewSubmissionFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemClickListener, Dialog.OnCancelListener { private final static String TAG = NewSubmissionFragment.class.toString(); private static final int REQUEST_IMAGE_CAPTURE = 1; private String photoPath; private ArrayList<Category> confirmedCategories; private ArrayList<Category> tempCategories; private Location location; private File photoFile; private Bitmap currentPhoto; private Button categorySelectButton; private View fragmentView; private EditText submissionDescription; private EditText submissionTitle; private ImageView photoThumbnail; private ImageView mapThumbnail; private LinearLayout categoriesLinearLayout; private ImageButton makeSubmissionButton; private ImageButton backButton; private CategoriesPopup popMaker; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.fragmentView = inflater.inflate(R.layout.fragment_new_submission, container, false); // find the views and add listeners findViews(); setupClickListeners(); fetchBundleFromArguments(); ArrayList<String> selectedCategories = new ArrayList<>(); this.categorySelectButton.setOnClickListener(this); this.confirmedCategories = new ArrayList<>(); this.tempCategories = new ArrayList<>(); ViewTreeObserver vto = this.mapThumbnail.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final LukeNetUtils lukeNetUtils = new LukeNetUtils(getMainActivity()); getMainActivity().runOnUiThread(new Runnable() { @Override public void run() { try { mapThumbnail.setImageBitmap(lukeNetUtils.getMapThumbnail(location, mapThumbnail.getWidth(), mapThumbnail.getHeight())); } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "run: ERROR SETTING IMAGE", e); } } }); photoThumbnail.getViewTreeObserver().removeOnGlobalLayoutListener(this); Log.e(TAG, "onGlobalLayout: photothumnailImageview dimensions:" + photoThumbnail.getWidth() + " x " + photoThumbnail.getHeight()); } }); return fragmentView; } /** * Finds the fragmen't views */ private void findViews() { this.makeSubmissionButton = (ImageButton) this.fragmentView.findViewById(R.id.button_tick_submit); this.backButton = (ImageButton) this.fragmentView.findViewById(R.id.button_back); this.categoriesLinearLayout = (LinearLayout) this.fragmentView.findViewById(R.id.categoriesLinearLayout); this.submissionDescription = (EditText) this.fragmentView .findViewById(R.id.newSubmissionEditTextDescrption); this.submissionTitle = (EditText) this.fragmentView.findViewById(R.id.newSubmissionEditTextTitle); this.submissionDescription.setImeOptions(EditorInfo.IME_ACTION_DONE); submissionTitle.setImeOptions(EditorInfo.IME_ACTION_DONE); this.categorySelectButton = (Button) fragmentView.findViewById(R.id.categorySelectButtonNewSubmission); this.photoThumbnail = (ImageView) this.fragmentView.findViewById(R.id.photoThumbnail); this.mapThumbnail = (ImageView) this.fragmentView.findViewById(R.id.newSubmissionMapThumbnail); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); createImageFile(); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.categorySelectButtonNewSubmission: makeCategoryListPopup(); break; case R.id.button_tick_submit: makeSubmission(); break; case R.id.photoThumbnail: dispatchTakePictureIntent(); break; case R.id.button_back: getMainActivity().onBackPressed(); break; case R.id.categories_accept_button: updateCategoryThumbnails(); this.popMaker.dismissCategoriesPopup(); break; } } private MainActivity getMainActivity() { return (MainActivity) getActivity(); } /** * Sets up the listeners */ private void setupClickListeners() { this.categoriesLinearLayout.setOnClickListener(this); this.makeSubmissionButton.setOnClickListener(this); this.photoThumbnail.setOnClickListener(this); this.backButton.setOnClickListener(this); } /** * Fetches the location from the bundle */ private void fetchBundleFromArguments() { Bundle b = getArguments(); // getMainActivity().getIntent().getExtras(); if (b != null) { this.location = new Location("jes"); this.location.setLatitude(b.getDouble("latitude")); this.location.setLongitude(b.getDouble("longitude")); this.location.setAltitude(b.getDouble("altitude")); Log.e(TAG, "onCreateView: bundle received: " + location.toString()); } } /** * Activates camera intent */ private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getMainActivity().getPackageManager()) != null) { // Create the File where the photo should go if (photoFile != null) { this.photoPath = photoFile.getAbsolutePath(); // Continue only if the File was successfully created Uri photoURI = FileProvider.getUriForFile(getMainActivity(), "com.luke.lukef.lukeapp", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } else { createImageFile(); dispatchTakePictureIntent(); } } } /** * Creates the image file from the taken image */ private void createImageFile() { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getMainActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = null; try { image = File.createTempFile(imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); } catch (IOException e) { Log.e(TAG, "createImageFile: ", e); } // Save a file: path for use with ACTION_VIEW intents this.photoPath = image.getAbsolutePath(); this.photoFile = image; } /* This method gets called once the camera intent is done. Gets the image that was taken with the camera, sets it in the imageview */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap imageBitmap = BitmapFactory.decodeFile(this.photoPath, options); if (imageBitmap != null) { try { Log.e(TAG, "onActivityResult: photo file before write" + this.photoFile.length()); FileOutputStream fo = new FileOutputStream(this.photoFile); imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fo); Log.e(TAG, "onActivityResult: photo file after write" + this.photoFile.length()); } catch (FileNotFoundException e) { Log.e(TAG, "onActivityResult: ", e); } imageBitmap = BitmapFactory.decodeFile(photoPath, options); if (imageBitmap != null) Log.e(TAG, "onActivityResult: photo exists, size : " + imageBitmap.getByteCount()); photoThumbnail.setImageBitmap(imageBitmap); this.currentPhoto = imageBitmap; } else { getMainActivity().makeToast("Error taking picture"); } } } /** * Makes the submission and pushes it to the server */ private void makeSubmission() { if (checkFieldsValidity()) { Submission newSub = new Submission(getMainActivity(), this.confirmedCategories, this.submissionTitle.getText().toString(), this.submissionDescription.getText().toString(), this.location); newSub.setFile(this.photoFile); if (this.currentPhoto != null) { newSub.setImage(this.currentPhoto); } if (newSub.submitToServer()) { // fetch the submissions again getMainActivity().startService(new Intent(getMainActivity(), SubmissionFetchService.class)); Log.e(TAG, "makeSubmission: Submission sent succesfully"); Bundle bundle = new Bundle(); bundle.putDouble("latitude", this.location.getLatitude()); bundle.putDouble("longitude", this.location.getLongitude()); getMainActivity().fragmentSwitcher(Constants.fragmentTypes.FRAGMENT_MAP, bundle); getMainActivity().makeToast("Success!"); } else { getMainActivity().makeToast("Error Submitting"); } } else { getMainActivity().makeToast("Fields are not valid!"); Log.e(TAG, "makeSubmission: FIELDS NOT VALID\nFIELDS NOT VALID"); } } /** * Checks that all the parameters for the submission are valid * * @return <b>true</b> if the parameters are valid, <b>false</b> if not */ private boolean checkFieldsValidity() { if (!TextUtils.isEmpty(this.submissionDescription.getText().toString())) { if (this.location != null) { if (this.confirmedCategories.size() > 0) { return true; } else { getMainActivity().makeToast("No categories selected."); return false; } } else { getMainActivity().makeToast("No location found."); return false; } } else { getMainActivity().makeToast("Description is required."); return false; } } /** * Creates the categories pop up, takes a copy of the current confirmed categories in case * user cancels their action. */ private void makeCategoryListPopup() { this.tempCategories.clear(); this.tempCategories = new ArrayList<>(this.confirmedCategories); Log.e(TAG, "makeCategoryListPopup: confirmed size " + this.confirmedCategories.size()); this.popMaker = new CategoriesPopup(getMainActivity(), this, this, this.confirmedCategories, this); this.popMaker.setupCategoriesPopup(); } /** * Handles updating the categories on the submission screen, based on user selection */ private void updateCategoryThumbnails() { this.categoriesLinearLayout.removeAllViews(); for (Category c : this.confirmedCategories) { ImageView categoryImg = new ImageView(this.getMainActivity()); categoryImg.setImageBitmap(c.getImage()); LinearLayout.LayoutParams make = new LinearLayout.LayoutParams(new LinearLayout.LayoutParams( this.categoriesLinearLayout.getHeight(), this.categoriesLinearLayout.getHeight())); categoryImg.setLayoutParams(make); this.categoriesLinearLayout.addView(categoryImg); } } // listens to the checkbox being clicked and adds/removes the selected categories @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { CheckBox checkBox = (CheckBox) view.findViewById(R.id.popup_categories_checkbox); boolean checked = checkBox.isChecked(); ((CheckBox) view.findViewById(R.id.popup_categories_checkbox)).setChecked(!checked); CategoriesPopup.ListViewAdapter listViewAdapter = (CategoriesPopup.ListViewAdapter) adapterView .getAdapter(); if (!checked) { this.confirmedCategories.add(listViewAdapter.getItem(i)); } else { this.confirmedCategories.remove(listViewAdapter.getItem(i)); } Log.e(TAG, "onItemClick: confirmed size after change " + this.confirmedCategories.size()); } @Override public void onCancel(DialogInterface dialogInterface) { Log.e(TAG, "onCancel: cancelled"); Log.e(TAG, "makeCategoryListPopup: temp size " + this.tempCategories.size()); this.confirmedCategories = new ArrayList<>(this.tempCategories); } }