Java tutorial
package com.joeyturczak.jtscanner.ui; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.preference.Preference; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewCompat; import android.support.v7.app.AlertDialog; import android.text.InputFilter; import android.view.KeyEvent; 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.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.google.firebase.database.ChildEventListener; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.Query; import com.google.zxing.ResultPoint; import com.google.zxing.client.android.BeepManager; import com.joeyturczak.jtscanner.R; import com.joeyturczak.jtscanner.adapters.ScanListAdapter; import com.joeyturczak.jtscanner.models.Asset; import com.joeyturczak.jtscanner.models.BaseAsset; import com.joeyturczak.jtscanner.models.TodayData; import com.joeyturczak.jtscanner.utils.FirebaseUtility; import com.joeyturczak.jtscanner.utils.Utility; import com.journeyapps.barcodescanner.BarcodeCallback; import com.journeyapps.barcodescanner.BarcodeResult; import com.journeyapps.barcodescanner.CompoundBarcodeView; import java.util.ArrayList; import java.util.List; /** * Copyright (C) 2015 Joey Turczak * * 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. */ public class ScannerFragment extends Fragment implements View.OnFocusChangeListener, TextView.OnEditorActionListener, Preference.OnPreferenceChangeListener { private static final int FOCUS_MACHINE = 100; private static final int FOCUS_HOTBOX = 101; private static final int FOCUS_COLDBOX = 102; private static final int TOAST_DUPLICATE = 200; private static final int TOAST_INCOMPLETE = 201; private static final int TOAST_NO_MANUAL = 202; private static final int TOAST_INVALID_MACHINE = 203; private static final int TOAST_INVALID_BOX = 204; private static final int TOAST_SAVE = 205; private static final int TOAST_EDITING = 206; private Toast mToast; private BeepManager mBeepManager; private CompoundBarcodeView mBarcodeView; private TextView mScanCount; private EditText mCurrentMachine; private EditText mCurrentHotBox; private EditText mCurrentColdBox; private ListView mScansListView; private ScanListAdapter mScanListAdapter; private Boolean mEditing = false; private int mEditId; private TodayData mTodayData; private List<BaseAsset> yesterdayAssets; private List<BaseAsset> twoDaysAgoAssets; private boolean mPrefTwoScan; private boolean mPrefExcludePrefix; private int mPrefBarcodeLength; private boolean mPrefAllowDuplicates; private boolean mPrefAllowManual; private String mPrefMachinePrefix; private String mPrefBoxPrefix; private boolean mPrefBeep; private boolean mPrefAllowEdit; private OnScanCompletedListener mScanCompletedListener; private OnScanEditedListener mScanEditedListener; private OnScanDeletedListener mScanDeletedListener; private BroadcastReceiver mDataReceiever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra("todayData")) { mTodayData = intent.getExtras().getParcelable("todayData"); updateUI(); } } }; private BarcodeCallback callback = new BarcodeCallback() { @Override public void barcodeResult(BarcodeResult result) { String resultText = result.getText(); if (mPrefBeep) { mBeepManager.playBeepSoundAndVibrate(); } if (resultText != null) { handleScanResult(resultText); } Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { mBarcodeView.decodeSingle(callback); } }, 1500); } @Override public void possibleResultPoints(List<ResultPoint> resultPoints) { } }; public ScannerFragment() { } public static ScannerFragment newInstance(TodayData todayData) { ScannerFragment fragment = new ScannerFragment(); Bundle args = new Bundle(); args.putParcelable("todayData", todayData); fragment.setArguments(args); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnScanCompletedListener) { mScanCompletedListener = (OnScanCompletedListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnScanCompleteListener"); } if (context instanceof OnScanEditedListener) { mScanEditedListener = (OnScanEditedListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnScanEditedListener"); } if (context instanceof OnScanDeletedListener) { mScanDeletedListener = (OnScanDeletedListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnScanDeletedListener"); } } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mTodayData = getArguments().getParcelable("todayData"); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_scanner, container, false); getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); setHasOptionsMenu(true); getActivity().setTitle(getString(R.string.scanner)); initializeViews(rootView); Query orderedScansListRef; DatabaseReference scansRef = FirebaseUtility.getScansRef(); orderedScansListRef = scansRef.orderByKey(); mScanListAdapter = new ScanListAdapter(getActivity(), BaseAsset.class, R.layout.scan_list_item, orderedScansListRef); mScansListView.setAdapter(mScanListAdapter); mScansListView.smoothScrollToPosition(mScanListAdapter.getCount() - 1); if (getArguments() != null) { mTodayData = getArguments().getParcelable("todayData"); updateUI(); } setListeners(); Utility.hideKeyboard(getActivity()); return rootView; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); MenuItem menuItem = menu.findItem(R.id.action_save_spreadsheet); menuItem.setVisible(false); } @Override public void onResume() { super.onResume(); LocalBroadcastManager.getInstance(getContext()).registerReceiver(mDataReceiever, new IntentFilter("Firebase")); mBeepManager = new BeepManager(getActivity()); mBeepManager.setBeepEnabled(true); mBarcodeView.resume(); mPrefExcludePrefix = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_exclude_prefix_key)); mPrefTwoScan = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_two_scan_key)); if (mPrefTwoScan) { loadTwoScanData(); } mPrefBarcodeLength = Utility.getBarcodeLength(getContext()); mPrefAllowDuplicates = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_allow_duplicates_key)); mPrefAllowManual = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_manual_entries_key)); mPrefMachinePrefix = Utility.getSharedPreferenceValueString(getContext(), getString(R.string.pref_machine_prefix_key)); mPrefBoxPrefix = Utility.getSharedPreferenceValueString(getContext(), getString(R.string.pref_box_prefix_key)); mPrefBeep = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_beep_key)); mPrefAllowEdit = Utility.getSharedPreferenceValueBoolean(getContext(), getString(R.string.pref_edit_key)); changeEditTextLength(mPrefBarcodeLength); } @Override public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mDataReceiever); mBeepManager.close(); mBarcodeView.pause(); } private void initializeViews(View rootView) { mBarcodeView = (CompoundBarcodeView) rootView.findViewById(R.id.barcodeScanner); mScanCount = (TextView) rootView.findViewById(R.id.scanCount); mCurrentMachine = (EditText) rootView.findViewById(R.id.edit_machine_value); mCurrentHotBox = (EditText) rootView.findViewById(R.id.edit_hot_box_value); mCurrentColdBox = (EditText) rootView.findViewById(R.id.edit_cold_box_value); mBarcodeView.setStatusText(""); mBarcodeView.decodeSingle(callback); mScansListView = (ListView) rootView.findViewById(R.id.scansListView); } private void setListeners() { mCurrentMachine.setOnFocusChangeListener(this); mCurrentColdBox.setOnFocusChangeListener(this); mCurrentHotBox.setOnFocusChangeListener(this); mCurrentMachine.setOnEditorActionListener(this); mCurrentColdBox.setOnEditorActionListener(this); mCurrentHotBox.setOnEditorActionListener(this); mScansListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mPrefAllowEdit) { if (!mEditing) { mCurrentMachine.requestFocus(); showEditDialog(position); } else { showScanToast(TOAST_EDITING, ""); } } } }); } private void showScanToast(int type, String resultText) { String message; switch (type) { case FOCUS_MACHINE: message = String.format(getString(R.string.toast_scan_machine_normal), resultText); break; case FOCUS_HOTBOX: message = String.format(getString(R.string.toast_scan_hot_box_normal), resultText); break; case FOCUS_COLDBOX: message = String.format(getString(R.string.toast_scan_cold_box_normal), resultText); break; case TOAST_DUPLICATE: switch (getCurrentFocus()) { case FOCUS_MACHINE: message = getString(R.string.toast_scan_exists_machine); break; case FOCUS_COLDBOX: case FOCUS_HOTBOX: message = getString(R.string.toast_scan_exists_box); break; default: message = getString(R.string.toast_scan_error_general); } break; case TOAST_INCOMPLETE: message = getString(R.string.toast_scan_incomplete); break; case TOAST_NO_MANUAL: message = getString(R.string.toast_scan_no_manual); break; case TOAST_INVALID_MACHINE: message = getString(R.string.toast_scan_machine_invalid); break; case TOAST_INVALID_BOX: message = getString(R.string.toast_scan_box_invalid); break; case TOAST_SAVE: message = getString(R.string.toast_scan_saved); break; case TOAST_EDITING: message = getString(R.string.toast_scan_editing); break; default: message = getString(R.string.toast_scan_error_general); break; } if (mToast != null) { mToast.cancel(); } mToast = Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT); mToast.show(); } public boolean onKeyDown(int keyCode, KeyEvent event) { return mBarcodeView.onKeyDown(keyCode, event); } public void saveAsset() { showScanToast(TOAST_SAVE, ""); String machine = mCurrentMachine.getText().toString(); String hotBox = mCurrentHotBox.getText().toString(); String coldBox = mCurrentColdBox.getText().toString(); final BaseAsset asset = new BaseAsset(machine, hotBox, coldBox); if (mEditing) { mScanEditedListener.onScanEdited(asset, mEditId); mEditing = false; } else { mScanCompletedListener.onScanCompleted(asset); } } public void handleScanResult(String resultText) { // Get the prefix on the barcode to exclude if there is one. String prefixToExclude; switch (getCurrentFocus()) { case FOCUS_MACHINE: prefixToExclude = mPrefMachinePrefix; if (resultText.startsWith(mPrefBoxPrefix)) { showScanToast(TOAST_INVALID_BOX, resultText); return; } break; case FOCUS_HOTBOX: case FOCUS_COLDBOX: prefixToExclude = mPrefBoxPrefix; if (resultText.startsWith(mPrefMachinePrefix)) { showScanToast(TOAST_INVALID_MACHINE, resultText); return; } break; default: prefixToExclude = ""; break; } if (!mPrefExcludePrefix) { prefixToExclude = ""; } resultText = resultText.replace(prefixToExclude, ""); // Check result for duplicates // If it is a duplicate, then we will show error message and stop here if (!mPrefAllowDuplicates) { if (checkForDuplicates(resultText)) { showScanToast(TOAST_DUPLICATE, resultText); return; } } EditText currentEditText = getCurrentFocusEditText(); if (currentEditText != null) { getCurrentFocusEditText().setText(resultText); showScanToast(getCurrentFocus(), resultText); } if (mPrefTwoScan) { String coldBox = checkForTwoScan(resultText); if (coldBox != null) { mCurrentHotBox.setText(coldBox); } } if (focusNext()) { saveAsset(); resetScanLayout(); updateUI(); } } public void resetScanLayout() { mCurrentMachine.setText(""); mCurrentHotBox.setText(""); mCurrentColdBox.setText(""); mCurrentMachine.requestFocus(); Utility.hideKeyboard(getActivity()); } private void showEditDialog(final int position) { //Make dialog to edit/delete scan String message = String.format(getString(R.string.dialog_edit_scan_message), String.valueOf(position + 1)); final AlertDialog alertDialog = new AlertDialog.Builder(getContext()) .setTitle(R.string.dialog_edit_scan_title).setMessage(message) .setIcon(Utility.tintDrawable(getContext(), R.drawable.ic_warning_black_24dp, R.color.accent)) .setPositiveButton(R.string.dialog_edit_scan_positive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mCurrentMachine.setText(mTodayData.getAssets().get(position).getMachine()); mCurrentHotBox.setText(mTodayData.getAssets().get(position).getHotBox()); mCurrentColdBox.setText(mTodayData.getAssets().get(position).getColdBox()); String text = String.format(getString(R.string.scan_count_label_edit), position + 1); mScanCount.setText(text); mEditId = position; mEditing = true; } }).setNeutralButton(R.string.dialog_edit_scan_delete, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AlertDialog deleteDialog = new AlertDialog.Builder(getContext()).setTitle("Delete Entry") .setMessage("Are you sure you want to delete this entry?") .setIcon(Utility.tintDrawable(getContext(), R.drawable.ic_warning_black_24dp, R.color.accent)) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Asset asset = new Asset(mTodayData.getAssets().get(position)); BaseAsset baseAsset = new BaseAsset(asset.getMachine(), asset.getHotBox(), asset.getColdBox()); mScanDeletedListener.onScanDeleted(baseAsset, position); } }).setNegativeButton("No", null).create(); deleteDialog.show(); } }).setNegativeButton(R.string.dialog_edit_scan_negative, null).create(); alertDialog.show(); } private void changeEditTextLength(int barcodeLength) { InputFilter[] inputFilters = new InputFilter[1]; inputFilters[0] = new InputFilter.LengthFilter(barcodeLength); mCurrentMachine.setFilters(inputFilters); mCurrentHotBox.setFilters(inputFilters); mCurrentColdBox.setFilters(inputFilters); } @Override public void onFocusChange(View v, boolean hasFocus) { EditText view = (EditText) v; LinearLayout viewLayout = (LinearLayout) view.getParent(); if (hasFocus) { viewLayout.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.primary_blue_grey)); view.setTextColor(ContextCompat.getColor(getContext(), R.color.accent)); ViewCompat.setElevation(view, 8); } else { viewLayout.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.primary_light_blue_grey)); view.setTextColor(ContextCompat.getColor(getContext(), android.R.color.black)); ViewCompat.setElevation(view, 0); } } @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT) { if (mPrefAllowManual) { handleScanResult(v.getText().toString()); } else { showScanToast(TOAST_NO_MANUAL, ""); } } return true; } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); if (key.equals(getString(R.string.pref_exclude_prefix_key))) { mPrefExcludePrefix = (boolean) newValue; return true; } else if (key.equals(getString(R.string.pref_two_scan_key))) { mPrefTwoScan = (boolean) newValue; if (mPrefTwoScan) { loadTwoScanData(); } return true; } else if (key.equals(getString(R.string.pref_barcode_length_key))) { mPrefBarcodeLength = (int) newValue; changeEditTextLength(mPrefBarcodeLength); return true; } else if (key.equals(getString(R.string.pref_allow_duplicates_key))) { mPrefAllowDuplicates = (boolean) newValue; return true; } else if (key.equals(getString(R.string.pref_manual_entries_key))) { mPrefAllowManual = (boolean) newValue; return true; } else if (key.equals(getString(R.string.pref_machine_prefix_key))) { mPrefMachinePrefix = (String) newValue; return true; } else if (key.equals(getString(R.string.pref_box_prefix_key))) { mPrefBoxPrefix = (String) newValue; return true; } else if (key.equals(getString(R.string.pref_beep_key))) { mPrefBeep = (boolean) newValue; return true; } else if (key.equals(getString(R.string.pref_edit_key))) { mPrefAllowEdit = (boolean) newValue; return true; } return false; } public interface OnScanCompletedListener { void onScanCompleted(BaseAsset asset); } public interface OnScanEditedListener { void onScanEdited(BaseAsset asset, int position); } public interface OnScanDeletedListener { void onScanDeleted(BaseAsset asset, int position); } private void updateUI() { mScanCount.setText( String.format(getString(R.string.scan_count_label), String.valueOf(mTodayData.getCount()))); mScansListView.smoothScrollToPosition(mScanListAdapter.getCount() - 1); } private int getCurrentFocus() { if (mCurrentMachine.hasFocus()) { return FOCUS_MACHINE; } else if (mCurrentHotBox.hasFocus()) { return FOCUS_HOTBOX; } else { return FOCUS_COLDBOX; } } private EditText getCurrentFocusEditText() { switch (getCurrentFocus()) { case FOCUS_MACHINE: return mCurrentMachine; case FOCUS_HOTBOX: return mCurrentHotBox; case FOCUS_COLDBOX: return mCurrentColdBox; } return null; } private String checkForTwoScan(String result) { if (getCurrentFocus() == FOCUS_MACHINE && !mEditing) { for (BaseAsset asset : yesterdayAssets) { if (result.equals(asset.getMachine())) { return asset.getColdBox(); } } for (BaseAsset asset : twoDaysAgoAssets) { if (result.equals(asset.getMachine())) { return asset.getColdBox(); } } } return null; } private boolean checkForDuplicates(String resultText) { List<String> ids = new ArrayList<>(); for (Asset asset : mTodayData.getAssets()) { switch (getCurrentFocus()) { case FOCUS_MACHINE: ids.add(asset.getMachine()); break; case FOCUS_HOTBOX: case FOCUS_COLDBOX: ids.add(asset.getColdBox()); ids.add(asset.getHotBox()); break; } } if (mEditing) { ids.remove(mCurrentMachine.getText().toString()); ids.remove(mCurrentHotBox.getText().toString()); ids.remove(mCurrentColdBox.getText().toString()); } for (String id : ids) { if (id.equals(resultText)) { return true; } } return false; } // Returns true if there are entries for every ID private boolean focusNext() { switch (getCurrentFocus()) { case FOCUS_MACHINE: mCurrentHotBox.requestFocus(); return false; case FOCUS_HOTBOX: mCurrentColdBox.requestFocus(); return false; case FOCUS_COLDBOX: if (mCurrentMachine.getText().toString().isEmpty()) { mCurrentMachine.requestFocus(); return false; } else if (mCurrentHotBox.getText().toString().isEmpty()) { mCurrentHotBox.requestFocus(); return false; } return true; default: return false; } } private void loadTwoScanData() { DatabaseReference yesterday = FirebaseDatabase.getInstance().getReference().child(Utility.getDateString(1)); DatabaseReference twoDays = FirebaseDatabase.getInstance().getReference().child(Utility.getDateString(2)); yesterdayAssets = new ArrayList<>(); twoDaysAgoAssets = new ArrayList<>(); yesterday.child("scans").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { yesterdayAssets.add(dataSnapshot.getValue(BaseAsset.class)); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }); twoDays.child("scans").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { twoDaysAgoAssets.add(dataSnapshot.getValue(BaseAsset.class)); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }); } }