2019-pracownia-programowani.../Lookify/app/src/main/java/com/example/lookifyv2/Results.java

266 lines
12 KiB
Java

package com.example.lookifyv2;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
//Klasa napisana z pomocą tutorialu z http://androidbash.com/connecting-android-app-to-a-database-using-php-and-mysql/.
public class Results extends AppCompatActivity {
List<Product> products;
List<Product> productstop6;
ProductsAdapter adapter;
String decodedCode;
Button button_favourite;
Button button_details;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_results);
//Do strzałki powrotu w górnej części ekranu.
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
products = new ArrayList<>();
productstop6 = new ArrayList<>();
getProductsFromDB(); //Ładuje wszystkie produkty z bazy do listy products, a następnie najbardziej podobne 6 do productstop6.
GridLayoutManager gridLayout = new GridLayoutManager(this, 2); //Recyclerview do implementacji potrzebuje managera,
//spancount to w praktyce wielkość kafelków.
recyclerView.setLayoutManager(gridLayout);
adapter = new ProductsAdapter(this, productstop6); //Adapter ładuje dane z jakiegoś datasetu aplikacji do kafelków.
recyclerView.setAdapter(adapter);
}
private void getProductsFromDB() {
@SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... Void) {
OkHttpClient client = new OkHttpClient(); //Zewnętrzna biblioteka wykorzystana do nawiązania połączenia.
Request request = new Request.Builder().url("http://192.168.0.88/products.php?id=0").build();
try {
Response response = client.newCall(request).execute();
JSONArray array = new JSONArray(response.body().string());
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
Product product = new Product(object.getInt("id"), object.getString("product_code"),
object.getString("product_name"), object.getString("product_image"),
object.getString("product_price"), object.getString("product_retailer"),
object.getString("product_colour"), object.getString("product_style"),
object.getString("product_collection"),
object.getString("product_type"), object.getString("product_sex"));
Results.this.products.add(product);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
//Dopiero po utworzeniu listy wszystkich produktów po stronie aplikacji wykonywane są dalsze operacje, stąd onPostExecute.
@Override
protected void onPostExecute(Void aVoid) {
adapter.notifyDataSetChanged(); //W praktyce informuje adapter, by odświeżył View którym zarząda, bo zmieniły się dane.
Bundle extras = getIntent().getExtras();
decodedCode = extras.getString("decodedcode");
//Ustalana jest lokacja zeskanowanego kodu w bazie; jeśli go tam nie ma, to następuje przeniesienie do ekranu błędu.
int foundPosition = -1;
for(int i = 0; i < products.size(); i++){
if(decodedCode.equals(products.get(i).getproductCode())){
foundPosition = i;
break;
}
}
if(foundPosition == -1){
Intent intent_DecodeFail = new Intent(Results.this, DecodeFail.class);
intent_DecodeFail.putExtra("status", "notindatabase");
startActivity(intent_DecodeFail);
finish();
}
ImageView imageView = findViewById(R.id.foundproductimage);
TextView foundProductPrice = findViewById(R.id.foundproductprice);
TextView foundProductName = findViewById(R.id.foundproductname);
TextView getFoundProductRetailer = findViewById(R.id.foundproductretailer);
//Do załadowania obrazka używana jest zewnętrzna biblioteka Glide.
Glide.with(Results.this).load(products.get(foundPosition).getImageLink()).into(imageView);
foundProductPrice.setText(products.get(foundPosition).getproductPrice());
foundProductName.setText(products.get(foundPosition).getproductName());
getFoundProductRetailer.setText(products.get(foundPosition).getproductRetailer());
button_favourite = findViewById(R.id.favourite_button);
button_favourite.getBackground().setColorFilter(0xFF67BAFF, PorterDuff.Mode.MULTIPLY);
button_details = findViewById(R.id.details_button);
button_details.getBackground().setColorFilter(0xFF67BAFF, PorterDuff.Mode.MULTIPLY);
final int finalFoundPosition = foundPosition; //Constant, jej wartości nie da się zmienić, tutaj wykorzystana bo były błędy
//ze zwykłym intem.
button_favourite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addFavourite(Results.this, finalFoundPosition);
}
});
button_details.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openDetails(finalFoundPosition);
}
});
getRecommendedProducts(finalFoundPosition); //zapełniane jest productstop6
}
};
asyncTask.execute();
}
private void openDetails(int position){
Intent intent_Details = new Intent(this, Popup.class);
Bundle bundle = new Bundle();
bundle.putSerializable("productimage", products.get(position).getImageLink());
bundle.putSerializable("productcode", products.get(position).getproductCode());
bundle.putSerializable("productname", products.get(position).getproductName());
bundle.putSerializable("productprice", products.get(position).getproductPrice());
bundle.putSerializable("productretailer", products.get(position).getproductRetailer());
bundle.putSerializable("productcolour", products.get(position).getproductColour());
bundle.putSerializable("productstyle", products.get(position).getproductStyle());
bundle.putSerializable("productcollection", products.get(position).getproductCollection());
intent_Details.putExtras(bundle);
startActivity(intent_Details);
}
//Do zapisywania obserwowanych produktów wykorzystany został niekonwencjonalnie depozyt preferencji użytkownika.
//Sprawdzana jest obecność kodu w depozycie i podejmowana odpowiednia akcja: albo dodanie go, albo wyświetlenie informacji,
//że już jest dodany.
private void addFavourite(Context context, int position){
SharedPreferences pref = MyApplication.getInstance().getSharedPreferences("Favourites", 0);
SharedPreferences.Editor editor = pref.edit();
Map<String, ?> codes = pref.getAll();
ArrayList<String> codeslist = Lists.newArrayList(codes.keySet());
int checker = 0;
for(int i = 0; i < codeslist.size(); i++){
if(products.get(position).getproductCode().equals(codeslist.get(i))){
checker = -1;
Toast.makeText(context, "Ten produkt jest już obserwowany!",
Toast.LENGTH_SHORT).show();
break;
}
}
if(checker == 0){
editor.putBoolean(products.get(position).getproductCode(), true);
editor.apply();
Toast.makeText(context, "Produkt \"" + products.get(position).getproductName() + "\" został dodany do Obserwowanych.",
Toast.LENGTH_SHORT).show();
}
}
//Prostu algorytm jest wykorzystywany, by stworzyć ranking produktów podobnych do tego zeskanowanego. Można znacznie rozbudować!
private void getRecommendedProducts(int foundpos){
HashMap<Integer, Integer> ranking = new HashMap<>();
for(int i = 0; i < products.size(); i++) {
if (products.get(foundpos).getId() != products.get(i).getId()
&& products.get(foundpos).getproductType().equals(products.get(i).getproductType())
&& products.get(foundpos).getproductSex().equals(products.get(i).getproductSex())) {
int counter = 0;
if (products.get(foundpos).getproductColour().equals(products.get(i).getproductColour())) {
counter++;
}
if (products.get(foundpos).getproductStyle().equals(products.get(i).getproductStyle())) {
counter++;
}
if (products.get(foundpos).getproductCollection().equals(products.get(i).getproductCollection())
|| products.get(foundpos).getproductCollection().equals("całoroczna")
|| products.get(i).getproductCollection().equals("całoroczna")) {
counter++;
}
ranking.put(i, counter);
}
}
//Odpowiednia informacja wyświetlana jest, jeśli nie znaleziono podobnych produktów.
//Do sortowania rankingu wykorzystywana jest zewnętrzna biblioteka Guava.
if(ranking.size() == 0){
TextView noproductsmessage = findViewById(R.id.noproductsmessage);
noproductsmessage.setVisibility(View.VISIBLE);
}
else{
Ordering<Map.Entry<Integer, Integer>> byMapValues = new Ordering<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> left, Map.Entry<Integer, Integer> right) { //Ustalany jest sposób sortowania.
return left.getValue().compareTo(right.getValue());
}
};
List<Map.Entry<Integer, Integer>> rankinglist = Lists.newArrayList(ranking.entrySet()); //Tworzona jest lista z zawartością mapy,
//by móc użyć metody sort (ze zdefiniowanym sposobem sortowania).
Collections.sort(rankinglist, byMapValues.reverse());
int i = 0;
while(i < 6 && i < ranking.size()) {
productstop6.add(products.get(rankinglist.get(i).getKey()));
i++;
}
}
}
//Kliknięcie strzałki powrotu kończy activity.
@Override
public boolean onSupportNavigateUp(){
finish();
return true;
}
}