This commit is contained in:
xdrm-brackets 2017-11-19 16:30:22 +01:00
parent 217d64e44e
commit 606cfe8aeb
13 changed files with 464 additions and 317 deletions

View File

@ -22,154 +22,272 @@ import javafx.util.Pair;
public class ApiCall implements Runnable, EventObserver { public class ApiCall implements Runnable, EventObserver {
//Methode appelée une fois que la requete s'est terminé /* (1) Attributes
private Callback callback; ---------------------------------------------------------*/
//connection HTTP /* (1) Request data */
private HttpURLConnection connection; private String httpMethod; // HTTP method
//méthode HTTP private HttpURLConnection httpConnection; // HTTP connection
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;
/* (2) Callback data */
public ApiCall(String URL, String method, Callback call) { private Boolean isDelayed = false; // If must wait for an event to start
this.callback = call; private Callback callback; // Callback ran when the request ends
this.method = method; 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 { try {
//création de la connection
URL url = new URL(URL); /* (1) Set the target URL */
this.connection = (HttpURLConnection) url.openConnection(); URL url = new URL(tUrl);
this.connection.setRequestMethod(method);
//si on fait une requete GET, il faut interdire l'envoie de donnée POST /* (2) Create the connection from URL */
if(method == "GET") { this.httpConnection = (HttpURLConnection) url.openConnection();
this.connection.setDoOutput(false);
}else { /* (3) Set the HTTP method */
this.connection.setDoOutput(true); this.httpConnection.setRequestMethod(this.httpMethod);
}
} catch (IOException e) { /* (4) If GET request, forbid POST data to be sent */
this.callback.onError(); 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(); /* (3) Add headers to the HTTP connection
while (it.hasNext()) { *
Map.Entry pair = (Map.Entry)it.next(); * @headerMap<HashMap<String,String>> HashMap containing the header pairs (key, value)
this.connection.setRequestProperty((String)pair.getKey(), (String)pair.getValue()); *
it.remove(); // évite les erreurs en cas de modification concurente ---------------------------------------------------------*/
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 @Override
public void run() { public void run() {
//si notre appel est delayed, on endort le thread
if(this.isDelayed) { /* (1) If delayed call -> sleep the thread, wait for an event
try { ---------------------------------------------------------*/
//on doit se synchroniser avec le thread avant de l'endormir if( this.isDelayed ){
synchronized(this.thread) {
this.thread.wait(); /* (1) Try to sleep the thread */
} try{
} catch (InterruptedException e) {
this.callback.onError(); synchronized(this.thread){ this.thread.wait(); }
/* (2) If cannot -> call callback onError() */
}catch(InterruptedException e){
this.callback.onError("cannot sleep thread");
return; 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; return;
}
/* (3) Manage response
---------------------------------------------------------*/
try { try {
//On envoie les paramètres POST si besoin
if(this.method != "GET") { /* (1) Get output stream to set Post data */
DataOutputStream wr = new DataOutputStream (connection.getOutputStream ()); if( this.httpMethod == "POST" ){
//TODO: implémenter la gestion des parametres POST
DataOutputStream wr = new DataOutputStream( httpConnection.getOutputStream() );
// TODO: manage post data
wr.flush (); wr.flush ();
wr.close (); wr.close ();
} }
//on récupère la réponse de la requete /* (2) Prepare to get response */
InputStream is = connection.getInputStream(); InputStream is = httpConnection.getInputStream(); // Get input stream
BufferedReader rd = new BufferedReader(new InputStreamReader(is)); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); // Input stream into readable buffer
String line; String line;
StringBuffer response = new StringBuffer(); StringBuffer response = new StringBuffer();
while((line = rd.readLine()) != null) {
/* (3) Get response */
while( ( line = rd.readLine() ) != null ){
response.append(line); response.append(line);
} }
rd.close(); rd.close();
//transformation en objet JSON /* (4) Parse JSON */
try { try{
//raplce null values by empty strings
JSONObject json = new JSONObject(response.toString().replaceAll(":null,", ":\"\",")); // {4.1} Specific: replace 'null' by empty strings //
this.callback.onSuccess(json); JSONObject parsedJson = new JSONObject(response.toString().replaceAll(":null,", ":\"\","));
}catch(JSONException e) {
this.callback.onError(); // {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() { 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); this.thread = new Thread(this);
/* (2) Start the thread */
this.thread.start(); this.thread.start();
} }
public void setAsDelayedCall(String eventID, String eventType, DelayedCallback call) {
//on enregistre tout ce dont on a besoin pour traiter l'évenement /* (6) Bind a delayed callback to the API call (chain another API call)
this.isDelayed = true; *
this.delayedCallback = call; * @eventID<String> Event ID
this.eventID = eventID; * @eventType<String> Event Type
this.eventType = eventType; * @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 @Override
public void handleEvent(Event e) { 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) { /* (1) If delayed callback and right event received
Pair<String,HashMap<String,String>> returned = this.delayedCallback.call(); ---------------------------------------------------------*/
if(this.method == "GET") { if( this.isDelayed && e.getObjectId() == this.eventID && e.getEventType() == this.eventType ){
try {
//on met a jour la connection HTTP avec la nouvelle URL en gardant l'ancienne méthode HTTP try{
HttpURLConnection newConnection = (HttpURLConnection) new URL(returned.getKey()).openConnection();
newConnection.setRequestMethod(this.connection.getRequestMethod()); /* (1) Call the callback and fetch its retusn URL+POST params */
newConnection.setDoOutput(this.connection.getDoOutput()); Pair<String, HashMap<String, String>> newData = this.delayedCallback.call();
this.connection = newConnection; URL newURL = new URL(newData.getKey());
//on a fini de mettre a jour la connection, le thread principal peut reprendre //HashMap<String, String> newPostData = newData.getValue();
synchronized(this.thread) {
this.thread.notify(); /* (2) Create the new connection */
} HttpURLConnection newConnection = (HttpURLConnection) newURL.openConnection();
} catch (IOException e1) {
//une erreur est survenue, on stope tout /* (3) Keep the same HTTP method as previous request */
this.callback.onError(); String lastHttpMethod = this.httpConnection.getRequestMethod();
this.shouldStop = true; newConnection.setRequestMethod( lastHttpMethod );
synchronized(this.thread) {
this.thread.notify(); /* (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; this.shouldStop = true;
synchronized(this.thread) { synchronized(this.thread){ this.thread.notify(); }
this.thread.notify();
}
} }
} }
} }

View File

@ -1,21 +1,20 @@
package Classes; package Classes;
public enum Category { public enum Category {
// business, entertainment, gaming, general, healthAndMedical, music, politics, scienceAndNature, sport, technology, all
all("all", "black"), all ( "all", "black" ),
business("business", "red"), business ( "business", "red" ),
entertainment("entertainment", "skyblue"), entertainment ( "entertainment", "skyblue" ),
gaming("gaming", "green"), gaming ( "gaming", "green" ),
health("health", "yellow"), health ( "health", "yellow" ),
music("music", "purple"), music ( "music", "purple" ),
sport("sport", "brown"), sport ( "sport", "brown" ),
science("science", "#f14405"), science ( "science", "#f14405" ),
nature("nature", "#16c668"), nature ( "nature", "#16c668" ),
economics("economics", "#d1991b"), economics ( "economics", "#d1991b" ),
politics("politics", "#6825f4"), politics ( "politics", "#6825f4" ),
technology("technology", "#1e7ebe"); technology ( "technology", "#1e7ebe" );
protected String color; protected String color;
protected String label; protected String label;
Category(String label, String color){ this.label = label; this.color = color; } Category(String label, String color){ this.label = label; this.color = color; }

View File

@ -1,22 +1,44 @@
package Classes; package Classes;
public class Event implements Interfaces.Event { public class Event implements Interfaces.Event {
/* (1) Attributes: Event identifiers
---------------------------------------------------------*/
private String objectId; private String objectId;
private String eventType; private String eventType;
/* (2) Constructs an event
*
* @objectId<String> Event ID
* @eventType<String> Type of event (arbitrary)
*
---------------------------------------------------------*/
public Event(String objectId, String eventType) { public Event(String objectId, String eventType) {
this.objectId = objectId; this.objectId = objectId;
this.eventType = eventType; 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;
} }
} }

View File

@ -4,8 +4,17 @@ import org.json.JSONObject;
public interface Callback { public interface Callback {
/* (1) Called when success state reached
*
* @response<JSONObject> The fetched response
*
---------------------------------------------------------*/
public void onSuccess(JSONObject response); public void onSuccess(JSONObject response);
public void onError();
/* (2) Called when success state cannot be reached
*
---------------------------------------------------------*/
public void onError(String errDesc);
} }

View File

@ -4,8 +4,10 @@ import java.util.HashMap;
import javafx.util.Pair; import javafx.util.Pair;
public interface DelayedCallback { public interface DelayedCallback {
public Pair<String,HashMap<String,String>> call(); public Pair<String,HashMap<String,String>> call();
} }

View File

@ -3,6 +3,6 @@ package Interfaces;
public interface Event { public interface Event {
public String getObjectId(); public String getObjectId();
public String getEventType(); public String getEventType();
} }

View File

@ -114,7 +114,11 @@ public class Article{
ImageView g_image = (ImageView) p_parent.getChildren().get(0); ImageView g_image = (ImageView) p_parent.getChildren().get(0);
/* (2) Update title */ /* (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+"'");
}
} }

View File

@ -10,38 +10,65 @@ import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane; import javafx.scene.layout.FlowPane;
public class HeaderMenu{ public class HeaderMenu{
/* Data */ /* (1) Attributes
---------------------------------------------------------*/
private ArrayList<ImageView> items; private ArrayList<ImageView> items;
private FlowPane parent; private FlowPane parent;
private EventObserver observer; private EventObserver observer;
/* Constructor */ /* Constructor */
/* (2) Constructor
*
* @p_parent<FlowPane> gParent element
* @observer<EventObserver> Main observer (to send event to)
*
---------------------------------------------------------*/
public HeaderMenu(FlowPane p_parent, EventObserver observer){ 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; 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) { public void addItem(String p_name, String p_image_uri) {
/* (1) Create ImageView */
ImageView menuItem = new ImageView(); ImageView menuItem = new ImageView();
menuItem.setImage(new Image(p_image_uri)); /* (2) Set useful attributes */
menuItem.setId("header_menu_item_"+p_name); menuItem.setImage(new Image(p_image_uri)); // image link
menuItem.getStyleClass().add("header_menu_item"); 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>() { menuItem.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override @Override
public void handle(MouseEvent event) { public void handle(MouseEvent event) {
HeaderMenu.this.observer.handleEvent(new Classes.Event(menuItem.getId(), "changeMainLayout")); HeaderMenu.this.observer.handleEvent(new Classes.Event(menuItem.getId(), "changeMainLayout"));
} }
}); });
/* (4) Add item to list */
this.items.add(menuItem); this.items.add(menuItem);
// Add to parent
/* (5) Add gitem to gparent */
parent.getChildren().add(menuItem); parent.getChildren().add(menuItem);
} }
} }

View File

@ -22,159 +22,178 @@ import model.NewsListModel;
import model.NewsModel; import model.NewsModel;
public class RootLayout extends Application implements EventObserver { 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 Stage root_stage;
private Scene root_scene; private Scene root_scene;
private AnchorPane root_layout; private AnchorPane root_layout;
/* (2) Local elements */
private FlowPane main_container; private FlowPane main_container;
private Article articles; private Article articles;
/* (2) Builds the stage, scene, ...
*
* @primary_stage<Stage> The primary stage to create in
*
---------------------------------------------------------*/
@Override @Override
public void start(Stage primary_stage) throws Exception { public void start(Stage primary_stage) throws Exception {
/* (1) Init. stage, scene and context
---------------------------------------------------------*/
/* (1) store primary stage + title it */ /* (1) store primary stage + title it */
this.root_stage = primary_stage; this.root_stage = primary_stage;
this.root_stage.setTitle("Inifiny Mail Client"); this.root_stage.setTitle("Inifiny Mail Client");
/* (2) Load the root layout*/ /* (2) Load the root layout*/
this.loadRootLayout(); this.loadRootLayout();
/* (3) Load the CSS CONTEXT */ /* (3) Load the CSS CONTEXT */
ContextBuilder.createContext(); ContextBuilder.createContext();
/* (3) Store container */ /* (3) Store container */
this.main_container = (FlowPane) this.root_scene.lookup("#container"); this.main_container = (FlowPane) this.root_scene.lookup("#container");
/* (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 controllers' views ---------------------------------------------------------*/
-------------------------------------*/ /* (1) Create HeaderMenu */
/* (1) Create header menu */
HeaderMenu hm = new HeaderMenu((FlowPane) this.root_scene.lookup("#header_menu"),this); HeaderMenu hm = new HeaderMenu((FlowPane) this.root_scene.lookup("#header_menu"),this);
hm.addItem("notification", "/src/header-notif.png"); hm.addItem("notification", "/src/header-notif.png");
hm.addItem("mail", "/src/header-mail.png"); hm.addItem("mail", "/src/header-mail.png");
hm.addItem("search", "/src/header-search.png"); hm.addItem("search", "/src/header-search.png");
hm.addItem("menu", "/src/header-menu.png"); hm.addItem("menu", "/src/header-menu.png");
/* (2) Create container */
this.articles = new Article(this.main_container, this);
/* (2) CSS
-------------------------------------*/
/* (1) #header */ /* (2) Init. articles controller */
new HeaderStyleSheet( this.root_scene.lookup("#header") ); this.articles = new Article(this.main_container, this);
/* (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) Loads the root layout and init. the scene-stage-pane
*
*
---------------------------------------------------------*/
public void loadRootLayout(){ public void loadRootLayout(){
try{ try{
/* (1) Load the root_disp.fxml */ /* (1) Load the root_disp.fxml */
FXMLLoader loader = new FXMLLoader(); FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/fxml/model.fxml")); loader.setLocation(getClass().getResource("/fxml/model.fxml"));
/* (2) Load the layout into the scene */ /* (2) Load the layout into the scene */
this.root_layout = (AnchorPane) loader.load(); this.root_layout = (AnchorPane) loader.load();
this.root_scene = new Scene(this.root_layout); this.root_scene = new Scene(this.root_layout);
/* (3) Add the scene to the stage */ /* (3) Add the scene to the stage */
this.root_stage.setScene(this.root_scene); this.root_stage.setScene(this.root_scene);
/* (4) Show the stage */ /* (4) Show the stage */
this.root_stage.show(); this.root_stage.show();
}catch(IOException e){ }catch(IOException e){
e.printStackTrace(); e.printStackTrace();
} }
} }
public static void main(String[] args) {
launch(args);
}
/* (4) Event dispatcher
*
* @e<Event> observed event received
*
---------------------------------------------------------*/
@Override @Override
public void handleEvent(Event e) { public void handleEvent(Event e) {
/* /*
HashMap<String,String> headers = new HashMap<String,String>(); HashMap<String,String> headers = new HashMap<String,String>();
headers.put("Referer", "http://www.wordreference.com"); headers.put("Referer", "http://www.wordreference.com");
ApiCall call = new ApiCall("http://api.wordreference.com/1/json/enfr/grin","GET",new Callback() { ApiCall call = new ApiCall("http://api.wordreference.com/1/json/enfr/grin","GET",new Callback() {
@Override @Override
public void onSuccess(JSONObject response) { public void onSuccess(JSONObject response) {
System.out.println(response.toString()); System.out.println(response.toString());
} }
@Override @Override
public void onError() { public void onError() {
System.out.println("APICall error"); System.out.println("APICall error");
} }
}); });
call.addHeaders(headers); call.addHeaders(headers);
call.send();*/ call.send();*/
switch(e.getEventType()){ switch( e.getEventType() ){
/* (1) On HeaderMenu.item.click -> search sources */
case "changeMainLayout": case "changeMainLayout":
this.handleMainLayoutChange(e.getObjectId()); this.handleMainLayoutChange(e.getObjectId());
break; break;
/* (2) articles.query.success -> display articles */
case "NewsQuerySuccess": case "NewsQuerySuccess":
System.out.println(NewsListModel.getInstance().getNews().size()+" News ont été trouvé");
// For each news
for( NewsModel news : NewsListModel.getInstance().getNews() ){ for( NewsModel news : NewsListModel.getInstance().getNews() ){
try{ try{
this.articles.addItem( news ); this.articles.addItem( news );
}catch(Exception e1){ }catch(Exception e1){
System.out.println("Cannot fetch article data"); 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());
break; break;
/* (3) articles.query.error -> display error ('no result') */
case "NewsQueryFailed": case "NewsQueryFailed":
System.out.println("une erreur est survenue"); System.out.println("une erreur est survenue");
break; break;
} }
} }
public void handleMainLayoutChange(String layout) { public void handleMainLayoutChange(String layout) {
NewsListModel.getInstance().addObserver("MainClass", this); NewsListModel.getInstance().addObserver("MainClass", this);
NewsListModel.getInstance().setCategory(Category.business); NewsListModel.getInstance().setCategory(Category.business);
NewsListModel.getInstance().setSortType(SortTypes.publishedAt); NewsListModel.getInstance().setSortType(SortTypes.publishedAt);
NewsListModel.getInstance().query("bitcoin"); NewsListModel.getInstance().query("bitcoin");
} }
} }

View File

@ -10,7 +10,7 @@
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?> <?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> <opaqueInsets>
<Insets /> <Insets />
</opaqueInsets> </opaqueInsets>

View File

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.Cursor?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.Image?> <?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?> <?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.FlowPane?> <?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
@ -54,63 +53,14 @@
</children> </children>
</AnchorPane> </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" /> <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"> <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">
<children> <content>
<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;"> <FlowPane id="container" fx:id="container" alignment="TOP_CENTER" columnHalignment="CENTER" prefWrapLength="1000.0">
<opaqueInsets> <opaqueInsets>
<Insets /> <Insets />
</opaqueInsets> </opaqueInsets>
<children> </FlowPane>
<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"> </content>
<image> </ScrollPane>
<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>
</children> </children>
</AnchorPane> </AnchorPane>

View File

@ -3,37 +3,35 @@ package model;
import Classes.Languages; import Classes.Languages;
public class LangModel { public class LangModel {
private static LangModel instance; private static LangModel instance;
private Languages fromLang = Languages.fr; private Languages fromLang = Languages.fr;
private Languages toLang = Languages.en; private Languages toLang = Languages.en;
private LangModel() { private LangModel(){}
}
public static LangModel getInstance(){
public static LangModel getInstance() { if( LangModel.instance == null )
if(LangModel.instance == null) {
LangModel.instance = new LangModel(); LangModel.instance = new LangModel();
}
return LangModel.instance; return LangModel.instance;
} }
public void setFromLang(Languages lang) { public void setFromLang(Languages lang){
this.fromLang = lang; this.fromLang = lang;
} }
public Languages getFromLang() { public Languages getFromLang(){
return this.fromLang; return this.fromLang;
} }
public void setToLang(Languages lang) { public void setToLang(Languages lang){
this.toLang = lang; this.toLang = lang;
} }
public Languages getToLang() { public Languages getToLang(){
return this.toLang; return this.toLang;
} }

View File

@ -6,7 +6,6 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import Classes.ApiCall; import Classes.ApiCall;
@ -74,7 +73,7 @@ public class NewsListModel implements Observable{
String lang = LangModel.getInstance().getToLang().name(); String lang = LangModel.getInstance().getToLang().name();
String URL = "http://beta.newsapi.org/v2/sources?language="+lang; String URL = "http://beta.newsapi.org/v2/sources?language="+lang;
//on rajoute la catégorie //on rajoute la catégorie
if(cat != Category.all) { if(cat != Category.all) {
URL += "&category="+cat.name(); URL += "&category="+cat.name();
} }
@ -100,7 +99,7 @@ public class NewsListModel implements Observable{
} }
@Override @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) //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.isRetreivingSources = false;
NewsListModel.this.apiError = true; NewsListModel.this.apiError = true;
@ -127,7 +126,7 @@ public class NewsListModel implements Observable{
//on créer l'URL de l'appel //on créer l'URL de l'appel
String lang = LangModel.getInstance().getToLang().name(); 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(); 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 //on ajoute la liste des sources a l'URL de la requete
if(NewsListModel.this.sources != null && NewsListModel.this.sources.size() != 0) { if(NewsListModel.this.sources != null && NewsListModel.this.sources.size() != 0) {
String sources = "&sources="; String sources = "&sources=";
@ -149,7 +148,7 @@ public class NewsListModel implements Observable{
for(int i = 0;i<arr.length();i++) { for(int i = 0;i<arr.length();i++) {
JSONObject jsonNews = (JSONObject)arr.get(i); JSONObject jsonNews = (JSONObject)arr.get(i);
NewsModel news = new NewsModel(); NewsModel news = new NewsModel();
news.setAuthor(jsonNews.getString("author")) news.setAuthor(jsonNews.getString("author"))
.setDescription(jsonNews.getString("description")) .setDescription(jsonNews.getString("description"))
.setTitle(jsonNews.getString("title")) .setTitle(jsonNews.getString("title"))
@ -157,16 +156,16 @@ public class NewsListModel implements Observable{
.setImageURL(jsonNews.getString("urlToImage")) .setImageURL(jsonNews.getString("urlToImage"))
.setSource(jsonNews.getJSONObject("source").getString("name")) .setSource(jsonNews.getJSONObject("source").getString("name"))
.addTag(NewsListModel.this.cat); .addTag(NewsListModel.this.cat);
try { try {
news.setDate(jsonNews.getString("publishedAt")); news.setDate(jsonNews.getString("publishedAt"));
}catch (ParseException e) { }catch (ParseException e) {
news.setDate(new Date()); news.setDate(new Date());
} }
NewsListModel.this.news.add(news); NewsListModel.this.news.add(news);
} }
NewsListModel.this.query = q; NewsListModel.this.query = q;
//ne pas oublier d'enlever l'api des observer, sinon il y a des risques de récurrence //ne pas oublier d'enlever l'api des observer, sinon il y a des risques de récurrence
NewsListModel.this.removeObserver("newsApiCall"); NewsListModel.this.removeObserver("newsApiCall");
@ -175,9 +174,9 @@ public class NewsListModel implements Observable{
} }
@Override @Override
public void onError() { public void onError(String errDesc) {
//L'appel a échoué :( //L'appel a échoué :(
System.out.println("Error"); System.out.println("Error: "+errDesc);
NewsListModel.this.removeObserver("newsApiCall"); NewsListModel.this.removeObserver("newsApiCall");
NewsListModel.this.notifyObservers(new Event("NewsModel","NewsQueryFailed")); NewsListModel.this.notifyObservers(new Event("NewsModel","NewsQueryFailed"));
} }
@ -227,13 +226,13 @@ public class NewsListModel implements Observable{
this.sortType = t; this.sortType = t;
this.query(this.query); this.query(this.query);
} }
public void notifyObservers(Event e) { public void notifyObservers(Event e) {
for(Object key : this.observers.keySet().toArray()) { for(Object key : this.observers.keySet().toArray()) {
this.observers.get(key).handleEvent(e); this.observers.get(key).handleEvent(e);
} }
} }
public ArrayList<NewsModel> getNews(){ public ArrayList<NewsModel> getNews(){
return this.news; return this.news;
} }
@ -247,7 +246,7 @@ public class NewsListModel implements Observable{
@Override @Override
public void removeObserver(String key) { public void removeObserver(String key) {
this.observers.remove(key); this.observers.remove(key);
} }