Java tutorial
/** * Copyright (C) 2016 - Franois LEPAROUX * <p/> * 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. * <p/> * 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. * <p/> * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.bde_eseo.eseomega.lacommande; import android.content.Context; import android.content.Intent; import android.graphics.PorterDuff; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import com.afollestad.materialdialogs.MaterialDialog; import com.melnykov.fab.FloatingActionButton; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.TimeZone; import fr.bde_eseo.eseomega.BuildConfig; import fr.bde_eseo.eseomega.Constants; import fr.bde_eseo.eseomega.R; import fr.bde_eseo.eseomega.lacommande.model.HistoryItem; import fr.bde_eseo.eseomega.listeners.RecyclerItemClickListener; import fr.bde_eseo.eseomega.profile.UserProfile; import fr.bde_eseo.eseomega.utils.ConnexionUtils; import fr.bde_eseo.eseomega.utils.EncryptUtils; import fr.bde_eseo.eseomega.utils.Utilities; /** * Created by Franois on 13/04/2015. * Permet d'afficher l'historique des commandes passes (synchronise avec le serveur ESEOmega) * Implmente un FloatingActionButton (Lollipop Style) afin de permettre l'ajout d'une nouvelle commande. * * Si en ligne -> refresh des donnes depuis le serveur et enregistrement du JSON * Si hors ligne -> chargement des donnes depuis le JSON enregistr */ public class OrderHistoryFragment extends Fragment { public OrderHistoryFragment() { } private RecyclerView recList; private ProgressBar progressBar, progressBarToken; private View viewToken; private FloatingActionButton fab; private MyHistoryAdapter mAdapter; private ArrayList<HistoryItem> historyList; private UserProfile userProfile; private String userLogin, userPass; private static Handler mHandler; private static final int RUN_UPDATE = 8000; private static final int RUN_START = 100; private static boolean run, backgrounded = false; private static boolean firstDisplay = true; private File cacheHistoryJSON; private HashMap<String, String> params; private long lastUpdate = 0; private Context context; private TextView tvNothing, tvNothing2, tvServiceInfo; private ImageView imgNothing; @Override public void onResume() { super.onResume(); firstDisplay = true; // Delay to update data run = true; if (progressBarToken != null) progressBarToken.setVisibility(View.INVISIBLE); if (fab != null) fab.setVisibility(View.VISIBLE); if (viewToken != null) viewToken.setVisibility(View.INVISIBLE); if (mHandler == null) { mHandler = new android.os.Handler(); mHandler.postDelayed(updateTimerThread, RUN_START); } else { mHandler.removeCallbacks(updateTimerThread); mHandler.postDelayed(updateTimerThread, RUN_START); } } @Override public void onPause() { if (mHandler != null) { mHandler.removeCallbacks(updateTimerThread); } run = false; super.onPause(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Find elements and attach listView / floating button View rootView = inflater.inflate(R.layout.fragment_cafet_history, container, false); // Get current fragment's context context = getActivity(); // Get user's data userProfile = new UserProfile(); userProfile.readProfilePromPrefs(getActivity()); userLogin = userProfile.getId(); userPass = userProfile.getPassword(); // Search for the listView, then set its adapter mAdapter = new MyHistoryAdapter(getActivity()); recList = (RecyclerView) rootView.findViewById(R.id.cardHistory); progressBar = (ProgressBar) rootView.findViewById(R.id.progressHistoryList); progressBarToken = (ProgressBar) rootView.findViewById(R.id.progressLoading); viewToken = rootView.findViewById(R.id.viewCircle); tvNothing = (TextView) rootView.findViewById(R.id.tvListNothing); tvNothing2 = (TextView) rootView.findViewById(R.id.tvListNothing2); tvServiceInfo = (TextView) rootView.findViewById(R.id.tvServiceInfo); imgNothing = (ImageView) rootView.findViewById(R.id.imgNoCommand); recList.setHasFixedSize(true); LinearLayoutManager llm = new LinearLayoutManager(getActivity()); llm.setOrientation(LinearLayoutManager.VERTICAL); recList.setLayoutManager(llm); recList.setAdapter(mAdapter); recList.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); progressBar.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.md_grey_500), PorterDuff.Mode.SRC_IN); progressBarToken.setVisibility(View.INVISIBLE); progressBarToken.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.md_white_1000), PorterDuff.Mode.SRC_IN); viewToken.setVisibility(View.INVISIBLE); tvNothing.setVisibility(View.GONE); tvNothing2.setVisibility(View.GONE); imgNothing.setVisibility(View.GONE); fab = (FloatingActionButton) rootView.findViewById(R.id.fab); fab.attachToRecyclerView(recList); // Change message if (userProfile.isCreated()) { tvNothing.setText(getActivity().getResources().getString(R.string.empty_header_history)); tvNothing2.setText(getActivity().getResources().getString(R.string.empty_desc_history)); } else { tvNothing.setText(getActivity().getResources().getString(R.string.empty_header_noconnect)); tvNothing2.setText(getActivity().getResources().getString(R.string.empty_desc_noconnect)); tvNothing.setVisibility(View.VISIBLE); tvNothing2.setVisibility(View.VISIBLE); imgNothing.setVisibility(View.VISIBLE); fab.setVisibility(View.GONE); } // Get file from cache directory String cachePath = getActivity().getCacheDir() + "/"; cacheHistoryJSON = new File(cachePath + "history.json"); // Create array and check online history historyList = new ArrayList<>(); // Delay to update data run = true; if (mHandler == null) { mHandler = new android.os.Handler(); mHandler.postDelayed(updateTimerThread, RUN_START); } else { mHandler.removeCallbacks(updateTimerThread); mHandler.postDelayed(updateTimerThread, RUN_START); } mAdapter.setHistoryArray(historyList); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TimeZone tz = Calendar.getInstance().getTimeZone(); String tzStr = tz.getID(); if (!userProfile.isCreated()) { new MaterialDialog.Builder(getActivity()).title("Vous n'tes pas connect").content( "Nous avons besoin de savoir qui vous tes avant de pouvoir vous laisser commander.") .negativeText("D'accord").cancelable(false).show(); } else if (false && !tzStr.equalsIgnoreCase(Constants.TZ_ID_PARIS)) { new MaterialDialog.Builder(getActivity()).title("Erreur").content( "L'accs la Cafet ne peut se faire depuis un autre pays que la France.\nEnvoyez nous une carte postale !") .negativeText("D'accord").cancelable(false).show(); } else { String versionName = BuildConfig.VERSION_NAME; /** Prepare data **/ long timestamp = System.currentTimeMillis() / 1000; // timestamp in seconds params = new HashMap<>(); params.put(getActivity().getResources().getString(R.string.client), userLogin); params.put(getActivity().getResources().getString(R.string.password), userPass); params.put(getActivity().getResources().getString(R.string.tstp), "" + timestamp); params.put(getActivity().getResources().getString(R.string.os), "" + Constants.APP_ID); params.put(getActivity().getResources().getString(R.string.version), "" + versionName); params.put(getActivity().getResources().getString(R.string.hash), EncryptUtils.sha256(getActivity().getResources().getString(R.string.MESSAGE_GET_TOKEN) + userLogin + userPass + timestamp + Constants.APP_ID)); /** Call async task **/ SyncTimeToken syncTimeToken = new SyncTimeToken(getActivity()); syncTimeToken.execute(Constants.URL_API_ORDER_PREPARE); } } }); recList.addOnItemTouchListener( new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Intent i = new Intent(getActivity(), OrderDetailsActivity.class); i.putExtra(Constants.KEY_ORDER_ID, historyList.get(position).getCommandNumber()); getActivity().startActivity(i); } })); // Who's cooking ? AsyncInfoService asyncInfoService = new AsyncInfoService(); asyncInfoService.execute(); return rootView; } /** * Asynctask to sync time and get token from server */ class SyncTimeToken extends AsyncTask<String, String, String> { String content; Context context; public SyncTimeToken(Context context) { this.context = context; } @Override protected void onPreExecute() { super.onPreExecute(); progressBarToken.setVisibility(View.VISIBLE); fab.setVisibility(View.INVISIBLE); viewToken.setVisibility(View.VISIBLE); } @Override protected String doInBackground(String... sUrl) { return ConnexionUtils.postServerData(Constants.URL_API_ORDER_PREPARE, params, context); } @Override protected void onPostExecute(String data) { String err = ""; int retCode = 0; String jsonToken = ""; /** Check if response is token, or an error **/ if (Utilities.isNetworkDataValid(data)) { // 64 : nb chars for a SHA256 value try { JSONObject obj = new JSONObject(data); retCode = obj.getInt("status"); err = obj.getString("cause"); if (retCode == 1) { jsonToken = obj.getJSONObject("data").getString("token"); } } catch (JSONException e) { e.printStackTrace(); } } // Check service answer if (retCode == 1) { // Success ! run = false; DataManager.getInstance().reset(); // reset data before writing in it DataManager.getInstance().setToken(jsonToken); // Sets the Token OrderTabsFragment fragment = new OrderTabsFragment(); FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); fragmentManager.beginTransaction() .setCustomAnimations(R.anim.slide_in, R.anim.slide_out, R.anim.slide_in, R.anim.slide_out) .replace(R.id.frame_container, fragment, Constants.TAG_FRAGMENT_ORDER_TABS) .addToBackStack("BACK").commit(); } else { retCode = -retCode; // -5 => error n5 String errorStr; switch (retCode) { case Constants.ERROR_TIMESTAMP: errorStr = Constants.ERROR_TIMESTAMP_STR; break; case Constants.ERROR_SERVICE_OUT: errorStr = Constants.ERROR_SERVICE_OUT_STR; break; case Constants.ERROR_USERREGISTER: errorStr = Constants.ERROR_USERREGISTER_STR; break; case Constants.ERROR_UNPAID: errorStr = Constants.ERROR_UNPAID_STR; break; case Constants.ERROR_APP_PB: errorStr = Constants.ERROR_APP_PB_STR; break; case Constants.ERROR_USER_BAN: errorStr = Constants.ERROR_USER_BAN_STR + err + ")"; break; case Constants.ERROR_BAD_VERSION: errorStr = Constants.ERROR_BAD_VERSION_STR; break; case Constants.ERROR_NETWORK: errorStr = Constants.ERROR_NETWORK_STR; break; default: errorStr = Constants.ERROR_UNKNOWN + " :\n" + data; break; } progressBarToken.setVisibility(View.INVISIBLE); fab.setVisibility(View.VISIBLE); viewToken.setVisibility(View.INVISIBLE); new MaterialDialog.Builder(context).title("Erreur").content(errorStr).cancelable(false) .negativeText("Fermer").show(); } } } /** * Background task to fetch data periodically from server */ private Runnable updateTimerThread = new Runnable() { public void run() { try { if (run && userProfile.isCreated()) {// && System.currentTimeMillis() - lastUpdate >= RUN_UPDATE) { run = false; SyncHistory syncHistory = new SyncHistory(); syncHistory.execute(); } } catch (NullPointerException e) { // Stop handler if fragment disappears mHandler.removeCallbacks(updateTimerThread); run = false; } } }; /** * Sync history, fetch data from server */ // Async Task Class class SyncHistory extends AsyncTask<String, String, String> { private HashMap<String, String> syncParam; @Override protected void onPreExecute() { super.onPreExecute(); syncParam = new HashMap<>(); run = false; if (firstDisplay) { progressBar.setVisibility(View.VISIBLE); recList.setVisibility(View.INVISIBLE); firstDisplay = false; } // Prepare param array syncParam.clear(); // in case of ... syncParam.put(context.getResources().getString(R.string.client), userLogin); syncParam.put(context.getResources().getString(R.string.password), userPass); syncParam.put(context.getResources().getString(R.string.hash), EncryptUtils.sha256( context.getResources().getString(R.string.MESSAGE_HISTORY_USER) + userLogin + userPass)); } @Override protected String doInBackground(String... args) { // Prepare JSON String String jsonStr; // Try to fetch data from server jsonStr = ConnexionUtils.postServerData(Constants.URL_API_ORDER_LIST, syncParam, context); // If data is empty if (!Utilities.isNetworkDataValid(jsonStr)) { // Fetch data from cache history if (cacheHistoryJSON.exists()) { jsonStr = Utilities.getStringFromFile(cacheHistoryJSON); } else { jsonStr = null; // force empty message } } else { // Else, there is a server response : phone is online try { JSONObject servJson = new JSONObject(jsonStr); // Check if there are no errors if (servJson.getInt("status") == 1) { jsonStr = servJson.getString("data"); } else { // bad password (-2) : display nothing jsonStr = null; } } catch (JSONException e) { e.printStackTrace(); // other error : display nothing jsonStr = null; } } if (jsonStr != null) { // Check / fflush data if (historyList == null) historyList = new ArrayList<>(); else historyList.clear(); // 1 if ok, -2 if not try { // Temporary's array ArrayList<HistoryItem> tempErrorArray = new ArrayList<>(); ArrayList<HistoryItem> tempReadyArray = new ArrayList<>(); ArrayList<HistoryItem> tempPreparingArray = new ArrayList<>(); ArrayList<HistoryItem> tempDoneArray = new ArrayList<>(); // parse JSON JSONObject json = new JSONObject(jsonStr); // Get all history items JSONArray jsData = new JSONArray(json.getString("history")); for (int i = 0; i < jsData.length(); i++) { JSONObject obj = jsData.getJSONObject(i); String sDate = obj.getString("datetime"); String parsed = obj.getString("resume"); parsed = parsed.replaceAll("<br>", ", "); HistoryItem hi = new HistoryItem(parsed, obj.getInt("status"), obj.getDouble("price"), sDate, obj.getInt("idcmd"), obj.getInt("modcmd"), obj.getString("strcmd"), false); switch (obj.getInt("status")) { case HistoryItem.STATUS_NOPAID: tempErrorArray.add(hi); break; case HistoryItem.STATUS_READY: tempReadyArray.add(hi); break; case HistoryItem.STATUS_PREPARING: tempPreparingArray.add(hi); break; case HistoryItem.STATUS_DONE: tempDoneArray.add(hi); break; } } // Add all data with headers if needed int aSize = tempErrorArray.size(); if (aSize > 0) { historyList.add(new HistoryItem("IMPAYE" + (aSize > 1 ? "S" : ""), false)); } historyList.addAll(tempErrorArray); if (aSize > 0) { historyList.add(new HistoryItem("Merci de venir la cafet' ou au BDE rgler ce" + (aSize > 1 ? "s" : "tte") + " commande" + (aSize > 1 ? "s" : "") + " au plus vite, sinon contactez-nous.", true)); } aSize = tempReadyArray.size(); if (aSize > 0) { historyList.add(new HistoryItem("PRTE" + (aSize > 1 ? "S" : ""), false)); } historyList.addAll(tempReadyArray); if (aSize > 0) { historyList.add(new HistoryItem("Merci de venir la cafet' chercher votre repas.", true)); } aSize = tempPreparingArray.size(); if (aSize > 0) { historyList.add(new HistoryItem("EN PRPARATION", false)); } historyList.addAll(tempPreparingArray); aSize = tempDoneArray.size(); if (aSize > 0) { historyList.add(new HistoryItem("TERMINE" + (aSize > 1 ? "S" : ""), false)); } historyList.addAll(tempDoneArray); // save data Utilities.writeStringToFile(cacheHistoryJSON, jsonStr); } catch (JSONException e) { e.printStackTrace(); } } return jsonStr; } // Once File is downloaded @Override protected void onPostExecute(String sJson) { progressBar.setVisibility(View.GONE); if (sJson != null) { mAdapter.notifyDataSetChanged(); // display "no command" or not if (historyList.size() == 0) { tvNothing.setVisibility(View.VISIBLE); tvNothing2.setVisibility(View.VISIBLE); imgNothing.setVisibility(View.VISIBLE); recList.setVisibility(View.GONE); } else { tvNothing.setVisibility(View.GONE); tvNothing2.setVisibility(View.GONE); imgNothing.setVisibility(View.GONE); recList.setVisibility(View.VISIBLE); } } else { tvNothing.setVisibility(View.VISIBLE); tvNothing2.setVisibility(View.VISIBLE); imgNothing.setVisibility(View.VISIBLE); recList.setVisibility(View.GONE); } mHandler.postDelayed(updateTimerThread, RUN_UPDATE); run = true; } } /** * Custom class to get information about cafeteria (current club ...) */ private class AsyncInfoService extends AsyncTask<String, String, String> { @Override protected String doInBackground(String... params) { return ConnexionUtils.postServerData(Constants.URL_API_INFO_SERVICE, null, getActivity()); } @Override protected void onPostExecute(String data) { super.onPostExecute(data); if (Utilities.isNetworkDataValid(data)) { try { JSONObject jsResp = new JSONObject(data); JSONObject jsData = jsResp.getJSONObject("data"); String service = jsData.getString("service"); if (service.length() > 0) { service = service.replace("\\n", "\n"); tvServiceInfo.setText(service); tvServiceInfo.setVisibility(View.VISIBLE); } else { tvServiceInfo.setVisibility(View.GONE); } } catch (JSONException e) { e.printStackTrace(); } } else { tvServiceInfo.setVisibility(View.GONE); } } } }