Compare commits
3 Commits
master
...
clean-code
Author | SHA1 | Date |
---|---|---|
xdrm-brackets | 50f1246a99 | |
xdrm-brackets | e680376c54 | |
xdrm-brackets | 606cfe8aeb |
|
@ -22,154 +22,272 @@ import javafx.util.Pair;
|
|||
|
||||
public class ApiCall implements Runnable, EventObserver {
|
||||
|
||||
//Methode appelée une fois que la requete s'est terminé
|
||||
private Callback callback;
|
||||
//connection HTTP
|
||||
private HttpURLConnection connection;
|
||||
//méthode HTTP
|
||||
private String method;
|
||||
//est ce que cet appel d'API attend un évenement pour se lancer
|
||||
private Boolean isDelayed = false;
|
||||
//Methode appellée une fois l'évenement arrivé, il retourne la nouvelle URL et les nouveaux parametres POST
|
||||
private DelayedCallback delayedCallback;
|
||||
//ID et Type de l'évenement attendu
|
||||
private String eventID;
|
||||
private String eventType;
|
||||
//objet contenant le thread de l'appel
|
||||
private Thread thread;
|
||||
//si une erreur surviens durant le delayedCallback, la boucle run doit tout arreter, cette variable sert a notifier run() de ça
|
||||
private Boolean shouldStop = false;
|
||||
/* (1) Attributes
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Request data */
|
||||
private String httpMethod; // HTTP method
|
||||
private HttpURLConnection httpConnection; // HTTP connection
|
||||
|
||||
|
||||
public ApiCall(String URL, String method, Callback call) {
|
||||
this.callback = call;
|
||||
this.method = method;
|
||||
/* (2) Callback data */
|
||||
private Boolean isDelayed = false; // If must wait for an event to start
|
||||
private Callback callback; // Callback ran when the request ends
|
||||
private DelayedCallback delayedCallback; // Callback ran when event handled, returns new URL and POST parameters
|
||||
private String eventID; // awaited event ID
|
||||
private String eventType; // awaited event Type
|
||||
private Thread thread; // main Thread
|
||||
private Boolean shouldStop = false; // If delayedCallback error -> run() must stop (notify)
|
||||
|
||||
|
||||
|
||||
/* (2) API call constructor
|
||||
*
|
||||
* @tUrl<String> Target URL
|
||||
* @tMethod<String> HTTP method
|
||||
* @callback<Callback> Callback run after the HTTP response received
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public ApiCall(String tUrl, String tMethod, Callback callback) {
|
||||
|
||||
/* (1) Store attributes
|
||||
---------------------------------------------------------*/
|
||||
this.callback = callback;
|
||||
this.httpMethod = tMethod;
|
||||
|
||||
|
||||
/* (2) Create the connection
|
||||
---------------------------------------------------------*/
|
||||
try {
|
||||
//création de la connection
|
||||
URL url = new URL(URL);
|
||||
this.connection = (HttpURLConnection) url.openConnection();
|
||||
this.connection.setRequestMethod(method);
|
||||
//si on fait une requete GET, il faut interdire l'envoie de donnée POST
|
||||
if(method == "GET") {
|
||||
this.connection.setDoOutput(false);
|
||||
}else {
|
||||
this.connection.setDoOutput(true);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
this.callback.onError();
|
||||
|
||||
/* (1) Set the target URL */
|
||||
URL url = new URL(tUrl);
|
||||
|
||||
/* (2) Create the connection from URL */
|
||||
this.httpConnection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
/* (3) Set the HTTP method */
|
||||
this.httpConnection.setRequestMethod(this.httpMethod);
|
||||
|
||||
/* (4) If GET request, forbid POST data to be sent */
|
||||
this.httpConnection.setDoOutput( httpMethod == "POST" );
|
||||
|
||||
}catch( IOException e ){
|
||||
|
||||
/* (x) If cannot create connection -> trigger the onError() callback */
|
||||
this.callback.onError("cannot create http connection");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addHeaders(HashMap<String,String> map) {
|
||||
Iterator it = map.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry pair = (Map.Entry)it.next();
|
||||
this.connection.setRequestProperty((String)pair.getKey(), (String)pair.getValue());
|
||||
it.remove(); // évite les erreurs en cas de modification concurente
|
||||
|
||||
/* (3) Add headers to the HTTP connection
|
||||
*
|
||||
* @headerMap<HashMap<String,String>> HashMap containing the header pairs (key, value)
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void addHeaders(HashMap<String,String> headerMap){
|
||||
|
||||
/* (1) Create iterator */
|
||||
Iterator it = headerMap.entrySet().iterator();
|
||||
|
||||
/* (2) Iterate until the end */
|
||||
while( it.hasNext() ){
|
||||
|
||||
/* (2.1) Get the current header pair */
|
||||
Map.Entry pair = (Map.Entry) it.next();
|
||||
|
||||
/* (2.2) Add the header to the @httpConnection */
|
||||
this.httpConnection.setRequestProperty(
|
||||
(String) pair.getKey(),
|
||||
(String) pair.getValue()
|
||||
);
|
||||
|
||||
/* (2.3) Avoid concurrency errors -> remove current header */
|
||||
it.remove();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//si notre appel est delayed, on endort le thread
|
||||
if(this.isDelayed) {
|
||||
try {
|
||||
//on doit se synchroniser avec le thread avant de l'endormir
|
||||
synchronized(this.thread) {
|
||||
this.thread.wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
this.callback.onError();
|
||||
|
||||
/* (1) If delayed call -> sleep the thread, wait for an event
|
||||
---------------------------------------------------------*/
|
||||
if( this.isDelayed ){
|
||||
|
||||
/* (1) Try to sleep the thread */
|
||||
try{
|
||||
|
||||
synchronized(this.thread){ this.thread.wait(); }
|
||||
|
||||
/* (2) If cannot -> call callback onError() */
|
||||
}catch(InterruptedException e){
|
||||
|
||||
this.callback.onError("cannot sleep thread");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//si il y a eu une erreur durant le delayedallback, on stope le thread
|
||||
if(this.shouldStop) {
|
||||
|
||||
|
||||
/* (2) If thread must stop (error during delayedCallback)
|
||||
---------------------------------------------------------*/
|
||||
if( this.shouldStop )
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* (3) Manage response
|
||||
---------------------------------------------------------*/
|
||||
try {
|
||||
//On envoie les paramètres POST si besoin
|
||||
if(this.method != "GET") {
|
||||
DataOutputStream wr = new DataOutputStream (connection.getOutputStream ());
|
||||
//TODO: implémenter la gestion des parametres POST
|
||||
|
||||
/* (1) Get output stream to set Post data */
|
||||
if( this.httpMethod == "POST" ){
|
||||
|
||||
DataOutputStream wr = new DataOutputStream( httpConnection.getOutputStream() );
|
||||
// TODO: manage post data
|
||||
wr.flush ();
|
||||
wr.close ();
|
||||
|
||||
}
|
||||
|
||||
//on récupère la réponse de la requete
|
||||
InputStream is = connection.getInputStream();
|
||||
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while((line = rd.readLine()) != null) {
|
||||
/* (2) Prepare to get response */
|
||||
InputStream is = httpConnection.getInputStream(); // Get input stream
|
||||
BufferedReader rd = new BufferedReader(new InputStreamReader(is)); // Input stream into readable buffer
|
||||
String line;
|
||||
StringBuffer response = new StringBuffer();
|
||||
|
||||
/* (3) Get response */
|
||||
while( ( line = rd.readLine() ) != null ){
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
rd.close();
|
||||
|
||||
//transformation en objet JSON
|
||||
try {
|
||||
//raplce null values by empty strings
|
||||
JSONObject json = new JSONObject(response.toString().replaceAll(":null,", ":\"\","));
|
||||
this.callback.onSuccess(json);
|
||||
}catch(JSONException e) {
|
||||
this.callback.onError();
|
||||
|
||||
/* (4) Parse JSON */
|
||||
try{
|
||||
|
||||
// {4.1} Specific: replace 'null' by empty strings //
|
||||
JSONObject parsedJson = new JSONObject(response.toString().replaceAll(":null,", ":\"\","));
|
||||
|
||||
// {4.2} If no error -> trigger onSuccess() //
|
||||
this.callback.onSuccess(parsedJson);
|
||||
|
||||
/* (5) If cannot parse JSON -> trigger onError() */
|
||||
}catch(JSONException jsonex){
|
||||
|
||||
this.callback.onError("json parse error");
|
||||
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
this.callback.onError();
|
||||
|
||||
/* (4) If cannot get response -> trigger onError()
|
||||
---------------------------------------------------------*/
|
||||
}catch (IOException ioex){
|
||||
|
||||
this.callback.onError("cannot get response");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* (5) Launches the run() loop in a Thread
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void send() {
|
||||
//send sert juste a lancer la boucle principale run()
|
||||
|
||||
/* (1) Create a new Thread for the current class */
|
||||
this.thread = new Thread(this);
|
||||
|
||||
/* (2) Start the thread */
|
||||
this.thread.start();
|
||||
|
||||
}
|
||||
|
||||
public void setAsDelayedCall(String eventID, String eventType, DelayedCallback call) {
|
||||
//on enregistre tout ce dont on a besoin pour traiter l'évenement
|
||||
this.isDelayed = true;
|
||||
this.delayedCallback = call;
|
||||
this.eventID = eventID;
|
||||
this.eventType = eventType;
|
||||
|
||||
|
||||
/* (6) Bind a delayed callback to the API call (chain another API call)
|
||||
*
|
||||
* @eventID<String> Event ID
|
||||
* @eventType<String> Event Type
|
||||
* @delayedCallback<DelayedCallback> Delayed callback
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void setAsDelayedCall(String eventID, String eventType, DelayedCallback delayedCallback) {
|
||||
|
||||
/* (1) Notify the delayed callback */
|
||||
this.isDelayed = true;
|
||||
this.delayedCallback = delayedCallback;
|
||||
this.eventID = eventID;
|
||||
this.eventType = eventType;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
//si on est un appel delayed et que l'évenement est le bon, on lance le delayedCallback
|
||||
if(this.isDelayed && e.getObjectId() == this.eventID && e.getEventType() == this.eventType) {
|
||||
Pair<String,HashMap<String,String>> returned = this.delayedCallback.call();
|
||||
if(this.method == "GET") {
|
||||
try {
|
||||
//on met a jour la connection HTTP avec la nouvelle URL en gardant l'ancienne méthode HTTP
|
||||
HttpURLConnection newConnection = (HttpURLConnection) new URL(returned.getKey()).openConnection();
|
||||
newConnection.setRequestMethod(this.connection.getRequestMethod());
|
||||
newConnection.setDoOutput(this.connection.getDoOutput());
|
||||
this.connection = newConnection;
|
||||
//on a fini de mettre a jour la connection, le thread principal peut reprendre
|
||||
synchronized(this.thread) {
|
||||
this.thread.notify();
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
//une erreur est survenue, on stope tout
|
||||
this.callback.onError();
|
||||
this.shouldStop = true;
|
||||
synchronized(this.thread) {
|
||||
this.thread.notify();
|
||||
}
|
||||
|
||||
/* (1) If delayed callback and right event received
|
||||
---------------------------------------------------------*/
|
||||
if( this.isDelayed && e.getObjectId() == this.eventID && e.getEventType() == this.eventType ){
|
||||
|
||||
try{
|
||||
|
||||
/* (1) Call the callback and fetch its retusn URL+POST params */
|
||||
Pair<String, HashMap<String, String>> newData = this.delayedCallback.call();
|
||||
URL newURL = new URL(newData.getKey());
|
||||
//HashMap<String, String> newPostData = newData.getValue();
|
||||
|
||||
/* (2) Create the new connection */
|
||||
HttpURLConnection newConnection = (HttpURLConnection) newURL.openConnection();
|
||||
|
||||
/* (3) Keep the same HTTP method as previous request */
|
||||
String lastHttpMethod = this.httpConnection.getRequestMethod();
|
||||
newConnection.setRequestMethod( lastHttpMethod );
|
||||
|
||||
/* (4) If GET request, forbid POST data to be sent */
|
||||
newConnection.setDoOutput( lastHttpMethod == "POST" );
|
||||
|
||||
/* (5) Manage POST data */
|
||||
if( this.httpMethod == "POST" ){
|
||||
// TODO: implement POST params management
|
||||
}
|
||||
}else {
|
||||
//TODO: implémenter les param POST
|
||||
|
||||
/* (6) Update current connection (replace with new) */
|
||||
this.httpConnection = newConnection;
|
||||
|
||||
/* (7) Replay main thread */
|
||||
synchronized(this.thread){ this.thread.notify(); }
|
||||
|
||||
|
||||
/* (1.8) If error while creating new connection -> onError()
|
||||
---------------------------------------------------------*/
|
||||
}catch( IOException ioe ){
|
||||
|
||||
/* (1) Launch onError() callback method */
|
||||
this.callback.onError("cannot create delayed http connection");
|
||||
|
||||
/* (2) Stop the main thread */
|
||||
this.shouldStop = true;
|
||||
synchronized(this.thread){ this.thread.notify(); }
|
||||
|
||||
}
|
||||
}else {
|
||||
this.callback.onError();
|
||||
|
||||
|
||||
/* (2) If not a delayed call, or not same event (id/type)
|
||||
---------------------------------------------------------*/
|
||||
}else{
|
||||
|
||||
/* (1) Launch onError() callback method */
|
||||
this.callback.onError("unknown event");
|
||||
|
||||
/* (2) Stop the main thread */
|
||||
this.shouldStop = true;
|
||||
synchronized(this.thread) {
|
||||
this.thread.notify();
|
||||
}
|
||||
synchronized(this.thread){ this.thread.notify(); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
package Classes;
|
||||
|
||||
public enum Category {
|
||||
// business, entertainment, gaming, general, healthAndMedical, music, politics, scienceAndNature, sport, technology, all
|
||||
|
||||
all("all", "black"),
|
||||
business("business", "red"),
|
||||
entertainment("entertainment", "skyblue"),
|
||||
gaming("gaming", "green"),
|
||||
health("health", "yellow"),
|
||||
music("music", "purple"),
|
||||
sport("sport", "brown"),
|
||||
science("science", "#f14405"),
|
||||
nature("nature", "#16c668"),
|
||||
economics("economics", "#d1991b"),
|
||||
politics("politics", "#6825f4"),
|
||||
technology("technology", "#1e7ebe");
|
||||
|
||||
all ( "all", "black" ),
|
||||
business ( "business", "red" ),
|
||||
entertainment ( "entertainment", "skyblue" ),
|
||||
gaming ( "gaming", "green" ),
|
||||
health ( "health", "yellow" ),
|
||||
music ( "music", "purple" ),
|
||||
sport ( "sport", "brown" ),
|
||||
science ( "science", "#f14405" ),
|
||||
nature ( "nature", "#16c668" ),
|
||||
economics ( "economics", "#d1991b" ),
|
||||
politics ( "politics", "#6825f4" ),
|
||||
technology ( "technology", "#1e7ebe" );
|
||||
|
||||
protected String color;
|
||||
protected String label;
|
||||
Category(String label, String color){ this.label = label; this.color = color; }
|
||||
|
|
|
@ -1,22 +1,44 @@
|
|||
package Classes;
|
||||
|
||||
public class Event implements Interfaces.Event {
|
||||
|
||||
|
||||
/* (1) Attributes: Event identifiers
|
||||
---------------------------------------------------------*/
|
||||
private String objectId;
|
||||
private String eventType;
|
||||
|
||||
|
||||
|
||||
/* (2) Constructs an event
|
||||
*
|
||||
* @objectId<String> Event ID
|
||||
* @eventType<String> Type of event (arbitrary)
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public Event(String objectId, String eventType) {
|
||||
|
||||
this.objectId = objectId;
|
||||
this.eventType = eventType;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
|
||||
/* (3) GET: @objectId
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public String getObjectId(){
|
||||
|
||||
return this.objectId;
|
||||
|
||||
}
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
|
||||
/* (4) GET: @eventType
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public String getEventType(){
|
||||
|
||||
return this.eventType;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package Classes.api;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import Classes.Event;
|
||||
import Interfaces.Callback;
|
||||
import model.NewsListModel;
|
||||
import model.NewsModel;
|
||||
|
||||
public class fetchArticles implements Callback{
|
||||
|
||||
private NewsListModel context;
|
||||
private String nextQuery;
|
||||
|
||||
public fetchArticles(NewsListModel context, String nextQuery){
|
||||
|
||||
this.context = context;
|
||||
this.nextQuery = nextQuery;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess(JSONObject response){
|
||||
|
||||
/* (1) Création des NewsModel depuis le json
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Init. news list */
|
||||
ArrayList<NewsModel> newsArr = new ArrayList<NewsModel>();
|
||||
|
||||
/* (2) Get article list */
|
||||
JSONArray article_list = response.getJSONArray("articles");
|
||||
|
||||
/* (3) For each article -> create corresponding NewsModel */
|
||||
for( int i = 0, il = article_list.length() ; i < il ; i++ ){
|
||||
|
||||
// {3.1} Get local copy of the JSON //
|
||||
JSONObject news_i = (JSONObject) article_list.get(i);
|
||||
|
||||
// {3.2} Create new NewsModel //
|
||||
NewsModel news = new NewsModel();
|
||||
|
||||
// {3.3} Set attributes //
|
||||
news.setAuthor( news_i.getString("author") )
|
||||
.setDescription( news_i.getString("description") )
|
||||
.setTitle( news_i.getString("title") )
|
||||
.setNewsURL( news_i.getString("url") )
|
||||
.setImageURL( news_i.getString("urlToImage") )
|
||||
.setSource( news_i.getJSONObject("source").getString("name") )
|
||||
.addTag( this.context.getCategory() );
|
||||
|
||||
// {3.4} Add the date (can throw Ex) //
|
||||
try { news.setDate(news_i.getString("publishedAt")); }
|
||||
catch( ParseException e ){ news.setDate(new Date()); }
|
||||
|
||||
// {3.5} Add to the set //
|
||||
newsArr.add(news);
|
||||
|
||||
}
|
||||
|
||||
/* (4) Apply to Context */
|
||||
this.context.setNews( newsArr );
|
||||
this.context.setQuery( this.nextQuery );
|
||||
|
||||
/* (5) Remove observers */
|
||||
this.context.removeObserver("newsApiCall");
|
||||
this.context.notifyObservers(new Event("NewsModel","NewsQuerySuccess"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errDesc) {
|
||||
|
||||
System.out.println("Error: "+errDesc);
|
||||
this.context.removeObserver("newsApiCall");
|
||||
this.context.notifyObservers(new Event("NewsModel","NewsQueryFailed"));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package Classes.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import Classes.Event;
|
||||
import Interfaces.Callback;
|
||||
import model.NewsListModel;
|
||||
|
||||
public class fetchSources implements Callback{
|
||||
|
||||
private NewsListModel context;
|
||||
private String nextQuery;
|
||||
|
||||
public fetchSources(NewsListModel context){
|
||||
|
||||
this.context = context;
|
||||
this.nextQuery = nextQuery;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess(JSONObject response){
|
||||
|
||||
//on récupère toutes les sources (dans la limite de 20 sources)
|
||||
JSONArray arr = response.getJSONArray("sources");
|
||||
|
||||
for(int i = 0;i<arr.length() && i<20;i++){
|
||||
|
||||
String source_i = (JSONObject) arr.get(i);
|
||||
|
||||
this.context.sources.add( source_i.getString("id") );
|
||||
|
||||
}
|
||||
|
||||
this.context.isRetreivingSources = false;
|
||||
this.context.apiError = false;
|
||||
|
||||
//tout s'est bien passé, on peut notifier du succès
|
||||
this.context.notifyObservers(new Event("NewsModel", "SourcesUpdated"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errDesc) {
|
||||
//on notifie de l'échec et on garde en mémoire le fait que l'on a échoué (afin d'annuler la recherche de news qui va suivre)
|
||||
this.context.isRetreivingSources = false;
|
||||
this.context.apiError = true;
|
||||
this.context.notifyObservers(new Event("NewsModel", "SourcesUpdateFailed"));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -4,8 +4,17 @@ import org.json.JSONObject;
|
|||
|
||||
public interface Callback {
|
||||
|
||||
/* (1) Called when success state reached
|
||||
*
|
||||
* @response<JSONObject> The fetched response
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void onSuccess(JSONObject response);
|
||||
|
||||
public void onError();
|
||||
|
||||
|
||||
|
||||
/* (2) Called when success state cannot be reached
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void onError(String errDesc);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import java.util.HashMap;
|
|||
|
||||
import javafx.util.Pair;
|
||||
|
||||
|
||||
|
||||
public interface DelayedCallback {
|
||||
|
||||
public Pair<String,HashMap<String,String>> call();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,6 @@ package Interfaces;
|
|||
public interface Event {
|
||||
|
||||
public String getObjectId();
|
||||
|
||||
|
||||
public String getEventType();
|
||||
}
|
||||
|
|
|
@ -17,130 +17,139 @@ import javafx.scene.text.Text;
|
|||
import model.NewsModel;
|
||||
|
||||
public class Article{
|
||||
|
||||
|
||||
/* Data */
|
||||
private ArrayList<AnchorPane> items;
|
||||
private FlowPane parent;
|
||||
private EventObserver observer;
|
||||
|
||||
|
||||
|
||||
|
||||
/* Constructor */
|
||||
public Article(FlowPane p_parent, EventObserver observer){
|
||||
this.parent = p_parent;
|
||||
this.items = new ArrayList<AnchorPane>();
|
||||
|
||||
/* (1) Attributes
|
||||
---------------------------------------------------------*/
|
||||
this.parent = p_parent;
|
||||
this.items = new ArrayList<AnchorPane>();
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
public void addItem(NewsModel model) throws IOException {
|
||||
|
||||
/* (1) Load the article_disp.fxml */
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(getClass().getResource("/fxml/article_disp.fxml"));
|
||||
|
||||
/* (2) Get the loaded item*/
|
||||
public synchronized void addItem(NewsModel model) throws IOException {
|
||||
|
||||
/* (1) Get the loaded item*/
|
||||
FXMLLoader loader = new FXMLLoader( getClass().getResource("/fxml/article_disp.fxml") );
|
||||
AnchorPane item = (AnchorPane) loader.load();
|
||||
|
||||
/* (3) Set content */
|
||||
|
||||
/* (2) Set content */
|
||||
this.gsetContent( model.getDescription(), item );
|
||||
|
||||
/* (4) Set date */
|
||||
|
||||
/* (3) Set date */
|
||||
this.gsetDate( model.getDate(), item );
|
||||
|
||||
/* (5) Set title */
|
||||
|
||||
/* (4) Set title */
|
||||
HBox headerContainer = (HBox) item.getChildren().get(3);
|
||||
this.gsetTitle( model.getTitle(), headerContainer );
|
||||
|
||||
/* (6) Set tags */
|
||||
|
||||
/* (5) Set tags */
|
||||
this.gsetTags( model.getTags(), headerContainer );
|
||||
|
||||
/* (7) Set image */
|
||||
|
||||
/* (6) Set image */
|
||||
this.gsetImage( model.getImageURL(), item );
|
||||
|
||||
/* (8) Bind event */
|
||||
|
||||
/* (7) Bind event */
|
||||
// item.setOnMousePressed(new EventHandler<MouseEvent>() {
|
||||
// @Override
|
||||
// public void handle(MouseEvent event) {
|
||||
// Article.this.observer.handleEvent(new Classes.Event(item.getId(),"changeMainLayout"));
|
||||
// }
|
||||
// });
|
||||
|
||||
/* (9) Add to the controller local */
|
||||
|
||||
/* (8) Add to the controller local */
|
||||
this.items.add(item);
|
||||
|
||||
/* (10) On bind au width du parent */
|
||||
|
||||
/* (9) On bind au width du parent */
|
||||
item.prefWidthProperty().bind(this.parent.widthProperty());
|
||||
item.maxWidthProperty().bind(this.parent.widthProperty());
|
||||
|
||||
/* (11) Add to parent (graphics) */
|
||||
|
||||
}
|
||||
|
||||
public void display(){
|
||||
|
||||
Platform.runLater(new Runnable(){
|
||||
public void run(){
|
||||
Article.this.parent.getChildren().add(item);
|
||||
|
||||
for( AnchorPane item : items )
|
||||
Article.this.parent.getChildren().add(item);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void gsetContent(String p_content, AnchorPane p_parent){
|
||||
/* (1) Get node */
|
||||
Text g_content = (Text) p_parent.getChildren().get(1);
|
||||
|
||||
|
||||
/* (2) Update content */
|
||||
g_content.setText(p_content);
|
||||
g_content.setText(p_content);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void gsetDate(Date p_date, AnchorPane p_parent){
|
||||
/* (1) Get node */
|
||||
Text g_date = (Text) p_parent.getChildren().get(2);
|
||||
|
||||
|
||||
/* (2) Update content */
|
||||
g_date.setText(p_date.toString());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void gsetTitle(String p_title, HBox p_parent){
|
||||
/* (1) Get node */
|
||||
Text g_title = (Text) p_parent.getChildren().get(0);
|
||||
|
||||
|
||||
/* (2) Update title */
|
||||
g_title.setText(p_title);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void gsetImage(String p_uri, AnchorPane p_parent){
|
||||
/* (1) Get node */
|
||||
ImageView g_image = (ImageView) p_parent.getChildren().get(0);
|
||||
|
||||
|
||||
/* (2) Update title */
|
||||
g_image.setImage(new Image(p_uri));
|
||||
try{
|
||||
g_image.setImage(new Image(p_uri));
|
||||
}catch(IllegalArgumentException wrongurlex){
|
||||
System.out.println("Cannot find image URL: '"+p_uri+"'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void gsetTags(ArrayList<Category> p_tags, HBox p_parent) throws IOException{
|
||||
|
||||
|
||||
/* (1) For each tag -> load a new one */
|
||||
for( Category tag : p_tags ){
|
||||
|
||||
/* (1.1) Create the container */
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(getClass().getResource("/fxml/article_tag_disp.fxml"));
|
||||
|
||||
/* (1.2) Load the tag elements */
|
||||
/* (1.1) Load the tag elements */
|
||||
FXMLLoader loader = new FXMLLoader( getClass().getResource("/fxml/article_tag_disp.fxml") );
|
||||
FlowPane g_tag = (FlowPane) loader.load();
|
||||
Text g_tagText = (Text) g_tag.getChildren().get(0);
|
||||
|
||||
/* (1.3) Update the tag name */
|
||||
|
||||
/* (1.2) Update the tag name */
|
||||
g_tagText.setText(tag.getLabel());
|
||||
|
||||
/* (1.4) Set the custom color */
|
||||
|
||||
/* (1.3) Set the custom color */
|
||||
g_tag.setStyle("-fx-background-color: "+tag.getColor());
|
||||
|
||||
/* (1.5) Ajout au parent*/
|
||||
|
||||
/* (1.4) Ajout au parent*/
|
||||
p_parent.getChildren().add(g_tag);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -10,38 +10,65 @@ import javafx.scene.input.MouseEvent;
|
|||
import javafx.scene.layout.FlowPane;
|
||||
|
||||
public class HeaderMenu{
|
||||
|
||||
/* Data */
|
||||
|
||||
/* (1) Attributes
|
||||
---------------------------------------------------------*/
|
||||
private ArrayList<ImageView> items;
|
||||
private FlowPane parent;
|
||||
private EventObserver observer;
|
||||
|
||||
|
||||
private FlowPane parent;
|
||||
private EventObserver observer;
|
||||
|
||||
|
||||
/* Constructor */
|
||||
|
||||
/* (2) Constructor
|
||||
*
|
||||
* @p_parent<FlowPane> gParent element
|
||||
* @observer<EventObserver> Main observer (to send event to)
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public HeaderMenu(FlowPane p_parent, EventObserver observer){
|
||||
this.parent = p_parent;
|
||||
this.items = new ArrayList<ImageView>();
|
||||
|
||||
this.parent = p_parent;
|
||||
this.items = new ArrayList<ImageView>();
|
||||
this.observer = observer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* (3) Add a new menu item (graphically too)
|
||||
*
|
||||
* @p_name<String> The new menu item's name
|
||||
* @p_image_uri<String> The new menu item's image link
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void addItem(String p_name, String p_image_uri) {
|
||||
|
||||
|
||||
/* (1) Create ImageView */
|
||||
ImageView menuItem = new ImageView();
|
||||
|
||||
menuItem.setImage(new Image(p_image_uri));
|
||||
menuItem.setId("header_menu_item_"+p_name);
|
||||
menuItem.getStyleClass().add("header_menu_item");
|
||||
|
||||
|
||||
/* (2) Set useful attributes */
|
||||
menuItem.setImage(new Image(p_image_uri)); // image link
|
||||
menuItem.setId("header_menu_item_"+p_name); // id
|
||||
menuItem.getStyleClass().add("header_menu_item"); // css .class
|
||||
|
||||
/* (3) Bind event -> 'changeMainLayout' */
|
||||
menuItem.setOnMousePressed(new EventHandler<MouseEvent>() {
|
||||
|
||||
@Override
|
||||
public void handle(MouseEvent event) {
|
||||
|
||||
HeaderMenu.this.observer.handleEvent(new Classes.Event(menuItem.getId(), "changeMainLayout"));
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/* (4) Add item to list */
|
||||
this.items.add(menuItem);
|
||||
// Add to parent
|
||||
|
||||
/* (5) Add gitem to gparent */
|
||||
parent.getChildren().add(menuItem);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,159 +22,180 @@ import model.NewsListModel;
|
|||
import model.NewsModel;
|
||||
|
||||
public class RootLayout extends Application implements EventObserver {
|
||||
|
||||
|
||||
public static void main(String[] args){ launch(args); }
|
||||
|
||||
|
||||
|
||||
/* (1) Attributes
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Root elements */
|
||||
private Stage root_stage;
|
||||
private Scene root_scene;
|
||||
private AnchorPane root_layout;
|
||||
|
||||
/* (2) Local elements */
|
||||
private FlowPane main_container;
|
||||
private Article articles;
|
||||
|
||||
|
||||
|
||||
|
||||
/* (2) Builds the stage, scene, ...
|
||||
*
|
||||
* @primary_stage<Stage> The primary stage to create in
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
@Override
|
||||
public void start(Stage primary_stage) throws Exception {
|
||||
|
||||
|
||||
/* (1) Init. stage, scene and context
|
||||
---------------------------------------------------------*/
|
||||
/* (1) store primary stage + title it */
|
||||
this.root_stage = primary_stage;
|
||||
this.root_stage.setTitle("Inifiny Mail Client");
|
||||
|
||||
|
||||
/* (2) Load the root layout*/
|
||||
this.loadRootLayout();
|
||||
|
||||
this.loadRootLayout();
|
||||
|
||||
/* (3) Load the CSS CONTEXT */
|
||||
ContextBuilder.createContext();
|
||||
|
||||
|
||||
/* (3) Store container */
|
||||
this.main_container = (FlowPane) this.root_scene.lookup("#container");
|
||||
|
||||
|
||||
|
||||
/* (1) Create controllers' views
|
||||
-------------------------------------*/
|
||||
/* (1) Create header menu */
|
||||
|
||||
|
||||
/* (2) Manage static stylesheet
|
||||
---------------------------------------------------------*/
|
||||
/* (1) #header */
|
||||
new HeaderStyleSheet( this.root_scene.lookup("#header") );
|
||||
|
||||
/* (2) #menu_container */
|
||||
new MenuStyleSheet( this.root_scene.lookup("#menu") );
|
||||
|
||||
/* (3) #submenu */
|
||||
new SubMenuStyleSheet( this.root_scene.lookup("#submenu") );
|
||||
|
||||
/* (4) #header_icon*/
|
||||
new HeaderIconStyleSheet( this.root_scene.lookup("#header_icon") );
|
||||
|
||||
|
||||
|
||||
/* (3) Manage controllers
|
||||
---------------------------------------------------------*/
|
||||
/* (1) Create HeaderMenu */
|
||||
HeaderMenu hm = new HeaderMenu((FlowPane) this.root_scene.lookup("#header_menu"),this);
|
||||
|
||||
|
||||
hm.addItem("notification", "/src/header-notif.png");
|
||||
hm.addItem("mail", "/src/header-mail.png");
|
||||
hm.addItem("search", "/src/header-search.png");
|
||||
hm.addItem("menu", "/src/header-menu.png");
|
||||
|
||||
|
||||
/* (2) Create container */
|
||||
this.articles = new Article(this.main_container, this);
|
||||
|
||||
|
||||
/* (2) CSS
|
||||
-------------------------------------*/
|
||||
|
||||
/* (1) #header */
|
||||
new HeaderStyleSheet( this.root_scene.lookup("#header") );
|
||||
|
||||
/* (2) #menu_container */
|
||||
new MenuStyleSheet( this.root_scene.lookup("#menu") );
|
||||
|
||||
/* (3) #submenu */
|
||||
new SubMenuStyleSheet( this.root_scene.lookup("#submenu") );
|
||||
|
||||
/* (4) #header_icon*/
|
||||
new HeaderIconStyleSheet( this.root_scene.lookup("#header_icon") );
|
||||
|
||||
/* (2) Init. articles controller */
|
||||
this.articles = new Article(this.main_container, this);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* (3) Loads the root layout and init. the scene-stage-pane
|
||||
*
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
public void loadRootLayout(){
|
||||
|
||||
|
||||
try{
|
||||
|
||||
|
||||
/* (1) Load the root_disp.fxml */
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
|
||||
|
||||
loader.setLocation(getClass().getResource("/fxml/model.fxml"));
|
||||
|
||||
|
||||
/* (2) Load the layout into the scene */
|
||||
this.root_layout = (AnchorPane) loader.load();
|
||||
this.root_scene = new Scene(this.root_layout);
|
||||
|
||||
|
||||
/* (3) Add the scene to the stage */
|
||||
this.root_stage.setScene(this.root_scene);
|
||||
|
||||
|
||||
/* (4) Show the stage */
|
||||
this.root_stage.show();
|
||||
|
||||
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
|
||||
/* (4) Event dispatcher
|
||||
*
|
||||
* @e<Event> observed event received
|
||||
*
|
||||
---------------------------------------------------------*/
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
/*
|
||||
HashMap<String,String> headers = new HashMap<String,String>();
|
||||
headers.put("Referer", "http://www.wordreference.com");
|
||||
|
||||
|
||||
ApiCall call = new ApiCall("http://api.wordreference.com/1/json/enfr/grin","GET",new Callback() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
System.out.println(response.toString());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
System.out.println("APICall error");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
call.addHeaders(headers);
|
||||
call.send();*/
|
||||
|
||||
switch(e.getEventType()){
|
||||
|
||||
|
||||
switch( e.getEventType() ){
|
||||
|
||||
/* (1) On HeaderMenu.item.click -> search sources */
|
||||
case "changeMainLayout":
|
||||
this.handleMainLayoutChange(e.getObjectId());
|
||||
break;
|
||||
|
||||
|
||||
/* (2) articles.query.success -> display articles */
|
||||
case "NewsQuerySuccess":
|
||||
System.out.println(NewsListModel.getInstance().getNews().size()+" News ont été trouvé");
|
||||
|
||||
// For each news
|
||||
|
||||
for( NewsModel news : NewsListModel.getInstance().getNews() ){
|
||||
|
||||
|
||||
try{
|
||||
|
||||
this.articles.addItem( news );
|
||||
|
||||
}catch(Exception e1){
|
||||
|
||||
System.out.println("Cannot fetch article data");
|
||||
e1.printStackTrace();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if( NewsListModel.getInstance().getNews().size() != 0 )
|
||||
System.out.println("La description du premier article est: "+NewsListModel.getInstance().getNews().get(0).getDescription());
|
||||
|
||||
this.articles.display();
|
||||
break;
|
||||
|
||||
|
||||
/* (3) articles.query.error -> display error ('no result') */
|
||||
case "NewsQueryFailed":
|
||||
System.out.println("une erreur est survenue");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void handleMainLayoutChange(String layout) {
|
||||
|
||||
NewsListModel.getInstance().addObserver("MainClass", this);
|
||||
NewsListModel.getInstance().setCategory(Category.business);
|
||||
NewsListModel.getInstance().setCategory(Category.science);
|
||||
NewsListModel.getInstance().setSortType(SortTypes.publishedAt);
|
||||
NewsListModel.getInstance().query("bitcoin");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<?import javafx.scene.text.Font?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
|
||||
<AnchorPane layoutX="65.0" layoutY="146.0" prefHeight="96.0" prefWidth="800.0" style="-fx-border-insets: 30; -fx-background-insets: 30; -fx-background-color: #fff; -fx-background-radius: 3; -fx-border-radius: 3; -fx-border-color: #ddd;">
|
||||
<AnchorPane layoutX="65.0" layoutY="146.0" prefHeight="120.0" prefWidth="800.0" style="-fx-border-insets: 30; -fx-background-insets: 30; -fx-background-color: #fff; -fx-background-radius: 3; -fx-border-radius: 3; -fx-border-color: #ddd;">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.Cursor?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
@ -54,63 +53,14 @@
|
|||
</children>
|
||||
</AnchorPane>
|
||||
<VBox id="submenu" fx:id="submenu" layoutX="234.0" layoutY="50.0" prefHeight="650.0" prefWidth="194.0" style="-fx-background-color: white;" AnchorPane.bottomAnchor="0.0" AnchorPane.topAnchor="50.0" />
|
||||
<FlowPane id="container" fx:id="container" alignment="TOP_CENTER" columnHalignment="CENTER" layoutX="421.0" layoutY="50.0" prefWrapLength="1000.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="429.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="50.0">
|
||||
<children>
|
||||
<AnchorPane fx:id="card1" prefHeight="118.0" style="-fx-border-insets: 10; -fx-background-insets: 10; -fx-background-color: #fff; -fx-background-radius: 3; -fx-border-radius: 3; -fx-border-color: #ddd;">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<children>
|
||||
<ImageView fitHeight="56.0" fitWidth="56.0" layoutX="-28.0" layoutY="38.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0" AnchorPane.topAnchor="20.0">
|
||||
<image>
|
||||
<Image url="@../src/menu_profile_1.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
<Text fill="#808080" fontSmoothingType="LCD" layoutX="118.0" layoutY="78.8439998626709" strokeType="OUTSIDE" strokeWidth="0.0" text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam posuere odio ut sem vestibulum, at tempus neque luctus. Curabitur quis tortor bibendum" wrappingWidth="653.0" AnchorPane.leftAnchor="96.0" AnchorPane.topAnchor="50.0">
|
||||
<font>
|
||||
<Font name="Lato Regular" size="12.0" />
|
||||
</font>
|
||||
</Text>
|
||||
<Text fill="#757575" layoutX="611.0" layoutY="37.0" strokeType="OUTSIDE" strokeWidth="0.0" text="10 days ago" textAlignment="RIGHT" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="20.0">
|
||||
<font>
|
||||
<Font name="Lato Bold" size="12.0" />
|
||||
</font>
|
||||
</Text>
|
||||
<HBox layoutX="118.0" layoutY="39.0" prefHeight="21.0" prefWidth="200.0" AnchorPane.leftAnchor="96.0" AnchorPane.topAnchor="20.0">
|
||||
<children>
|
||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Some Article Title">
|
||||
<font>
|
||||
<Font name="Lato Bold" size="17.0" />
|
||||
</font>
|
||||
</Text>
|
||||
<FlowPane alignment="CENTER" columnHalignment="CENTER" prefHeight="20.0" style="-fx-background-color: red; -fx-background-radius: 3;">
|
||||
<children>
|
||||
<Text fill="WHITE" fontSmoothingType="LCD" strokeType="OUTSIDE" strokeWidth="0.0" text="nature">
|
||||
<font>
|
||||
<Font name="Lato Regular" size="14.0" />
|
||||
</font>
|
||||
<FlowPane.margin>
|
||||
<Insets />
|
||||
</FlowPane.margin>
|
||||
</Text>
|
||||
</children>
|
||||
<HBox.margin>
|
||||
<Insets left="10.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets left="5.0" right="5.0" />
|
||||
</padding>
|
||||
</FlowPane>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND" />
|
||||
</cursor>
|
||||
</AnchorPane>
|
||||
</children>
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets></FlowPane>
|
||||
<ScrollPane id="container_scroll" fx:id="container_scroll" hbarPolicy="NEVER" layoutX="428.0" layoutY="50.0" pannable="true" prefHeight="650.0" prefWidth="852.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="428.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="50.0">
|
||||
<content>
|
||||
<FlowPane id="container" fx:id="container" alignment="TOP_CENTER" columnHalignment="CENTER" prefWrapLength="1000.0">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
</FlowPane>
|
||||
</content>
|
||||
</ScrollPane>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
|
|
|
@ -3,37 +3,35 @@ package model;
|
|||
import Classes.Languages;
|
||||
|
||||
public class LangModel {
|
||||
|
||||
|
||||
private static LangModel instance;
|
||||
|
||||
|
||||
private Languages fromLang = Languages.fr;
|
||||
private Languages toLang = Languages.en;
|
||||
|
||||
|
||||
private LangModel() {
|
||||
}
|
||||
|
||||
public static LangModel getInstance() {
|
||||
if(LangModel.instance == null) {
|
||||
private Languages toLang = Languages.en;
|
||||
|
||||
|
||||
private LangModel(){}
|
||||
|
||||
public static LangModel getInstance(){
|
||||
if( LangModel.instance == null )
|
||||
LangModel.instance = new LangModel();
|
||||
}
|
||||
|
||||
|
||||
return LangModel.instance;
|
||||
}
|
||||
|
||||
public void setFromLang(Languages lang) {
|
||||
|
||||
public void setFromLang(Languages lang){
|
||||
this.fromLang = lang;
|
||||
}
|
||||
|
||||
public Languages getFromLang() {
|
||||
|
||||
public Languages getFromLang(){
|
||||
return this.fromLang;
|
||||
}
|
||||
|
||||
public void setToLang(Languages lang) {
|
||||
|
||||
public void setToLang(Languages lang){
|
||||
this.toLang = lang;
|
||||
}
|
||||
|
||||
public Languages getToLang() {
|
||||
|
||||
public Languages getToLang(){
|
||||
return this.toLang;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
package model;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import Classes.ApiCall;
|
||||
import Classes.Category;
|
||||
import Classes.Event;
|
||||
import Classes.SortTypes;
|
||||
import Classes.api.fetchArticles;
|
||||
import Interfaces.Callback;
|
||||
import Interfaces.DelayedCallback;
|
||||
import Interfaces.EventObserver;
|
||||
|
@ -24,66 +22,62 @@ public class NewsListModel implements Observable{
|
|||
//instance du singleton
|
||||
private static NewsListModel instance;
|
||||
|
||||
//Clé d'API
|
||||
private String APIKey;
|
||||
//catégorie choise parl'utilisateur
|
||||
private Category cat = Category.all;
|
||||
//est ce que le modele est en train de récupérer les sources
|
||||
private Boolean isRetreivingSources = false;
|
||||
//est ce que l'api a rencontré une erreur
|
||||
private Boolean apiError = false;
|
||||
//liste des sources
|
||||
private ArrayList<String> sources;
|
||||
//liste des observers
|
||||
private HashMap<String,EventObserver> observers;
|
||||
//liste des news finale
|
||||
private ArrayList<NewsModel> news;
|
||||
//critère de tri
|
||||
private SortTypes sortType = SortTypes.relevancy;
|
||||
//critère de recherche de l'utilisateur
|
||||
private String query = "";
|
||||
|
||||
private String APIKey; // Clé d'API
|
||||
private Category cat = Category.all; // catégorie choise parl'utilisateur
|
||||
private Boolean isRetreivingSources = false; // est ce que le modele est en train de récupérer les sources
|
||||
private Boolean apiError = false; // est ce que l'api a rencontré une erreur
|
||||
private ArrayList<String> sources; // liste des sources
|
||||
private HashMap<String,EventObserver> observers; // liste des observers
|
||||
private ArrayList<NewsModel> news; // liste des news finale
|
||||
private SortTypes sortType = SortTypes.relevancy; // critère de tri
|
||||
private String query = ""; // critère de recherche de l'utilisateur
|
||||
|
||||
private NewsListModel(String APIKey) {
|
||||
|
||||
this.APIKey = APIKey;
|
||||
|
||||
//comme la liste des sources retournée par l'API est de 20 éléments max, pas la peine de déclarer une arraylist sans borne
|
||||
this.sources = new ArrayList<String>(20);
|
||||
this.observers = new HashMap<String,EventObserver>();
|
||||
this.news = new ArrayList<NewsModel>();
|
||||
this.sources = new ArrayList<String>(20);
|
||||
this.observers = new HashMap<String, EventObserver>();
|
||||
this.news = new ArrayList<NewsModel>();
|
||||
|
||||
}
|
||||
|
||||
public static NewsListModel getInstance() {
|
||||
if(NewsListModel.instance == null) {
|
||||
|
||||
if( NewsListModel.instance == null )
|
||||
NewsListModel.instance = new NewsListModel("0e72f765c5c84313ae31a5a7e9e61735");
|
||||
}
|
||||
|
||||
return NewsListModel.instance;
|
||||
|
||||
}
|
||||
|
||||
public void setCategory(Category cat) {
|
||||
|
||||
if(cat == Category.all) {
|
||||
if( cat == Category.all ){
|
||||
this.sources = null;
|
||||
this.cat = cat;
|
||||
return;
|
||||
}
|
||||
|
||||
//on vide la liste des sources
|
||||
this.sources = new ArrayList<String>(20);
|
||||
this.sources.clear();
|
||||
this.cat = cat;
|
||||
|
||||
//on créé l'URL d'appel de l'API
|
||||
String lang = LangModel.getInstance().getToLang().name();
|
||||
String URL = "http://beta.newsapi.org/v2/sources?language="+lang;
|
||||
|
||||
//on rajoute la catégorie
|
||||
if(cat != Category.all) {
|
||||
//on rajoute la catégorie
|
||||
if( cat != Category.all )
|
||||
URL += "&category="+cat.name();
|
||||
}
|
||||
|
||||
//on rajoute la clé d'api
|
||||
URL += "&apiKey="+this.APIKey;
|
||||
|
||||
//création de l'appel
|
||||
ApiCall api = new ApiCall(URL,"GET",new Callback() {
|
||||
// Création de l'appel
|
||||
ApiCall api = new ApiCall(URL, "GET", new Callback(){
|
||||
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
|
@ -100,7 +94,7 @@ public class NewsListModel implements Observable{
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
public void onError(String errDesc) {
|
||||
//on notifie de l'échec et on garde en mémoire le fait que l'on a échoué (afin d'annuler la recherche de news qui va suivre)
|
||||
NewsListModel.this.isRetreivingSources = false;
|
||||
NewsListModel.this.apiError = true;
|
||||
|
@ -109,6 +103,7 @@ public class NewsListModel implements Observable{
|
|||
}
|
||||
|
||||
});
|
||||
|
||||
this.isRetreivingSources = true;
|
||||
api.send();
|
||||
}
|
||||
|
@ -127,7 +122,7 @@ public class NewsListModel implements Observable{
|
|||
//on créer l'URL de l'appel
|
||||
String lang = LangModel.getInstance().getToLang().name();
|
||||
String URL = "http://beta.newsapi.org/v2/everything?apiKey="+this.APIKey+"&language="+lang+"&q="+q+"&sortBy="+this.sortType.name();
|
||||
|
||||
|
||||
//on ajoute la liste des sources a l'URL de la requete
|
||||
if(NewsListModel.this.sources != null && NewsListModel.this.sources.size() != 0) {
|
||||
String sources = "&sources=";
|
||||
|
@ -139,50 +134,10 @@ public class NewsListModel implements Observable{
|
|||
URL += sources;
|
||||
}
|
||||
|
||||
ApiCall api = new ApiCall(URL,"GET",new Callback() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
//on parcours le JSON en créant les objets NewsModel corespondant
|
||||
NewsListModel.this.news = new ArrayList<NewsModel>();
|
||||
JSONArray arr = response.getJSONArray("articles");
|
||||
for(int i = 0;i<arr.length();i++) {
|
||||
JSONObject jsonNews = (JSONObject)arr.get(i);
|
||||
NewsModel news = new NewsModel();
|
||||
|
||||
news.setAuthor(jsonNews.getString("author"))
|
||||
.setDescription(jsonNews.getString("description"))
|
||||
.setTitle(jsonNews.getString("title"))
|
||||
.setNewsURL(jsonNews.getString("url"))
|
||||
.setImageURL(jsonNews.getString("urlToImage"))
|
||||
.setSource(jsonNews.getJSONObject("source").getString("name"))
|
||||
.addTag(NewsListModel.this.cat);
|
||||
|
||||
try {
|
||||
news.setDate(jsonNews.getString("publishedAt"));
|
||||
}catch (ParseException e) {
|
||||
news.setDate(new Date());
|
||||
}
|
||||
|
||||
NewsListModel.this.news.add(news);
|
||||
}
|
||||
|
||||
NewsListModel.this.query = q;
|
||||
//ne pas oublier d'enlever l'api des observer, sinon il y a des risques de récurrence
|
||||
NewsListModel.this.removeObserver("newsApiCall");
|
||||
NewsListModel.this.notifyObservers(new Event("NewsModel","NewsQuerySuccess"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
//L'appel a échoué :(
|
||||
System.out.println("Error");
|
||||
NewsListModel.this.removeObserver("newsApiCall");
|
||||
NewsListModel.this.notifyObservers(new Event("NewsModel","NewsQueryFailed"));
|
||||
}
|
||||
|
||||
});
|
||||
ApiCall api = new ApiCall(URL,
|
||||
"GET",
|
||||
new fetchArticles(this, q)
|
||||
);
|
||||
|
||||
//on delay uniquement si on est en train de récupérer les sources
|
||||
if(this.isRetreivingSources) {
|
||||
|
@ -227,13 +182,21 @@ public class NewsListModel implements Observable{
|
|||
this.sortType = t;
|
||||
this.query(this.query);
|
||||
}
|
||||
|
||||
|
||||
public void notifyObservers(Event e) {
|
||||
for(Object key : this.observers.keySet().toArray()) {
|
||||
this.observers.get(key).handleEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setQuery(String query){
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public void setNews(ArrayList<NewsModel> news){
|
||||
this.news = news;
|
||||
}
|
||||
|
||||
public ArrayList<NewsModel> getNews(){
|
||||
return this.news;
|
||||
}
|
||||
|
@ -247,7 +210,7 @@ public class NewsListModel implements Observable{
|
|||
@Override
|
||||
public void removeObserver(String key) {
|
||||
this.observers.remove(key);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue