OSDN Git Service

Evernoteサーバからのサムネイル取得を別スレッドで順次実行するようにした
authoryuki <kimaira7@gmail.com>
Wed, 30 Oct 2013 07:44:59 +0000 (16:44 +0900)
committeryuki <kimaira7@gmail.com>
Tue, 5 Nov 2013 08:54:13 +0000 (17:54 +0900)
src/cx/fbn/nevernote/gui/RensoNoteList.java
src/cx/fbn/nevernote/gui/RensoNoteListItem.java
src/cx/fbn/nevernote/gui/Thumbnailer.java
src/cx/fbn/nevernote/signals/ENThumbnailSignal.java [new file with mode: 0644]
src/cx/fbn/nevernote/threads/ENRelatedNotesRunner.java
src/cx/fbn/nevernote/threads/ENThumbnailRunner.java [new file with mode: 0644]

index c3f829b..5307edb 100644 (file)
@@ -23,10 +23,12 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import com.evernote.edam.type.Note;
 import com.trolltech.qt.QThread;
+import com.trolltech.qt.core.QFile;
 import com.trolltech.qt.core.QSize;
 import com.trolltech.qt.core.Qt.MouseButton;
 import com.trolltech.qt.gui.QAction;
@@ -40,6 +42,7 @@ import cx.fbn.nevernote.Global;
 import cx.fbn.nevernote.NeverNote;
 import cx.fbn.nevernote.sql.DatabaseConnection;
 import cx.fbn.nevernote.threads.ENRelatedNotesRunner;
+import cx.fbn.nevernote.threads.ENThumbnailRunner;
 import cx.fbn.nevernote.threads.SyncRunner;
 import cx.fbn.nevernote.utilities.ApplicationLogger;
 import cx.fbn.nevernote.utilities.Pair;
@@ -48,7 +51,7 @@ public class RensoNoteList extends QListWidget {
        private final DatabaseConnection conn;
        private final ApplicationLogger logger;
        private final HashMap<QListWidgetItem, String> rensoNoteListItems;
-       private final List<RensoNoteListItem> rensoNoteListTrueItems;
+       private final HashMap<String, RensoNoteListItem> rensoNoteListTrueItems;
        private String rensoNotePressedItemGuid;
        private final QAction openNewTabAction;
        private final QAction starAction;
@@ -61,6 +64,8 @@ public class RensoNoteList extends QListWidget {
        private final ENRelatedNotesRunner enRelatedNotesRunner;
        private final QThread enRelatedNotesThread;
        private final HashMap<String, List<String>> enRelatedNotesCache;        // Evernote関連ノートのキャッシュ<guid, 関連ノートリスト>
+       private final ENThumbnailRunner enThumbnailRunner;
+       private final QThread enThumbnailThread;
        private String guid;
        private int allPointSum;
 
@@ -82,8 +87,14 @@ public class RensoNoteList extends QListWidget {
                this.enRelatedNotesThread = new QThread(enRelatedNotesRunner, "ENRelatedNotes Thread");
                this.getEnRelatedNotesThread().start();
                
+               this.enThumbnailRunner = new ENThumbnailRunner(this.logger);
+               this.enThumbnailRunner.enThumbnailSignal.getENThumbnailFinished.connect(this, "enThumbnailComplete(String)");
+               this.enThumbnailRunner.limitSignal.rateLimitReached.connect(parent, "informRateLimit(Integer)");
+               this.enThumbnailThread = new QThread(enThumbnailRunner, "ENThumbnail Thread");
+               this.enThumbnailThread.start();
+               
                rensoNoteListItems = new HashMap<QListWidgetItem, String>();
-               rensoNoteListTrueItems = new ArrayList<RensoNoteListItem>();
+               rensoNoteListTrueItems = new HashMap<String, RensoNoteListItem>();
                
                this.itemPressed.connect(this, "rensoNoteItemPressed(QListWidgetItem)");
                
@@ -255,6 +266,9 @@ public class RensoNoteList extends QListWidget {
        private void addRensoNoteList(HashMap<String, Integer> History){
                logger.log(logger.EXTREME, "Entering RensoNoteList.addRensoNoteList");
                
+               enThumbnailRunner.setUser(Global.getUserInformation());
+               enThumbnailRunner.setServerUrl(Global.getServer());
+               
                String currentNoteGuid = new String(parent.getCurrentNoteGuid());
                
                // スター付きノートとスター無しノートを分ける
@@ -307,6 +321,16 @@ public class RensoNoteList extends QListWidget {
                                
                                // 存在していて、かつ関連度0でなければノート情報を取得して連想ノートリストに追加
                                if (isNoteActive && maxNum > 0) {
+                                       // Evernoteサムネイルが取得済みか確認。未取得ならサムネイル取得スレッドにキュー
+                                       // TODO 今はとりあえずresディレクトリを確認。あとでデータベースにカラムを追加する
+                                       if (Global.isConnected) {
+                                               String thumbnailName = Global.getFileManager().getResDirPath("enThumbnail-" + maxGuid + ".png");
+                                               QFile thumbnail = new QFile(thumbnailName);
+                                               if (!thumbnail.exists()) {
+                                                       enThumbnailRunner.addGuid(maxGuid);
+                                               }
+                                       }
+                                       
                                        // スター付きか確認
                                        boolean isStared;
                                        isStared = conn.getStaredTable().existNote(currentNoteGuid, maxGuid);
@@ -317,7 +341,7 @@ public class RensoNoteList extends QListWidget {
                                        this.addItem(item);
                                        this.setItemWidget(item, myItem);
                                        rensoNoteListItems.put(item, maxGuid);
-                                       rensoNoteListTrueItems.add(myItem);
+                                       rensoNoteListTrueItems.put(maxGuid, myItem);
                                } else {
                                        break;
                                }
@@ -374,8 +398,7 @@ public class RensoNoteList extends QListWidget {
        // コンテキストメニューが閉じられた時
        @SuppressWarnings("unused")
        private void contextMenuHidden() {
-               for (int i = 0; i < rensoNoteListTrueItems.size(); i++) {
-                       RensoNoteListItem item = rensoNoteListTrueItems.get(i);
+               for (RensoNoteListItem item : rensoNoteListTrueItems.values()) {
                        item.setDefaultBackground();
                }
        }
@@ -445,12 +468,17 @@ public class RensoNoteList extends QListWidget {
        public boolean stopThread() {
                logger.log(logger.HIGH, "Entering RensoNoteList.stopThread");
                
-               if (enRelatedNotesRunner.addStop()) {
-                       logger.log(logger.HIGH, "RensoNoteList.stopThread succeeded");
-                       return true;
+               if (!enRelatedNotesRunner.addStop()) {
+                       logger.log(logger.HIGH, "RensoNoteList.stopThread failed(enRelatedNotesRunner)");
+                       return false;
                }
-               logger.log(logger.HIGH, "RensoNoteList.stopThread failed");
-               return false;
+               if (!enThumbnailRunner.addStop()) {
+                       logger.log(logger.HIGH, "RensoNoteList.stopThread failed(enThumbnailRunner)");
+                       return false;
+               }
+               
+               logger.log(logger.HIGH, "RensoNoteList.stopThread succeeded");
+               return true;
        }
 
        public QThread getEnRelatedNotesThread() {
@@ -475,4 +503,23 @@ public class RensoNoteList extends QListWidget {
                
                return dstHistory;
        }
+       
+       /**
+        * Evernoteサムネイルの取得が完了
+        * 
+        * @param guid 現在開いているノートのguid
+        */
+       @SuppressWarnings("unused")
+       private void enThumbnailComplete(String guid) {
+               logger.log(logger.HIGH, "Entering RensoNoteList.enThumbnailComplete");
+               
+               for (Map.Entry<String, RensoNoteListItem> e : rensoNoteListTrueItems.entrySet()) {
+                       // サムネイル取得が完了したノートが現在の連想ノートリストに表示されていたら再描画
+                       if (guid.equals(e.getKey())) {
+                               e.getValue().repaint();
+                       }
+               }
+               
+               logger.log(logger.HIGH, "Leaving RensoNoteList.enThumbnailComplete");
+       }
 }
index fc576d8..fbbbf25 100644 (file)
@@ -150,7 +150,7 @@ public class RensoNoteListItem extends QWidget{
                painter.setPen(tmpPen);
                // サムネイル
                QImage img;
-               String thumbnailName = Global.getFileManager().getResDirPath("thumbnail-" + noteGuid + ".png");
+               String thumbnailName = Global.getFileManager().getResDirPath("enThumbnail-" + noteGuid + ".png");
                QFile thumbnail = new QFile(thumbnailName);
                if (!thumbnail.exists()) {
                        img = new QImage();
index 7f2e2bf..2c9e82a 100644 (file)
@@ -1,23 +1,5 @@
 package cx.fbn.nevernote.gui;\r
 \r
-import java.io.FileInputStream;\r
-import java.io.FileNotFoundException;\r
-import java.io.IOException;\r
-import java.io.UnsupportedEncodingException;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.apache.http.HttpResponse;\r
-import org.apache.http.NameValuePair;\r
-import org.apache.http.client.ClientProtocolException;\r
-import org.apache.http.client.HttpClient;\r
-import org.apache.http.client.entity.UrlEncodedFormEntity;\r
-import org.apache.http.client.methods.HttpPost;\r
-import org.apache.http.impl.client.DefaultHttpClient;\r
-import org.apache.http.message.BasicNameValuePair;\r
-import org.apache.http.util.EntityUtils;\r
-\r
-import com.evernote.edam.type.User;\r
 import com.trolltech.qt.core.QBuffer;\r
 import com.trolltech.qt.core.QByteArray;\r
 import com.trolltech.qt.core.QIODevice;\r
@@ -34,10 +16,8 @@ import com.trolltech.qt.gui.QPainter;
 import com.trolltech.qt.webkit.QWebPage;\r
 \r
 import cx.fbn.nevernote.Global;\r
-import cx.fbn.nevernote.oauth.OAuthTokenizer;\r
 import cx.fbn.nevernote.sql.DatabaseConnection;\r
 import cx.fbn.nevernote.threads.ThumbnailRunner;\r
-import cx.fbn.nevernote.utilities.AESEncrypter;\r
 import cx.fbn.nevernote.utilities.ApplicationLogger;\r
 import cx.fbn.nevernote.utilities.ListManager;\r
 \r
@@ -144,56 +124,5 @@ public class Thumbnailer extends QObject {
                QByteArray b = buffer.buffer();\r
                conn.getNoteTable().setThumbnail(guid, b);\r
                conn.getNoteTable().setThumbnailNeeded(guid, false);\r
-               \r
-               // サムネイルをEvernoteサーバから取得\r
-               User user = Global.getUserInformation();\r
-               String serverUrl = Global.getServer();\r
-               String shardId = user.getShardId();\r
-               if (shardId == null || shardId.equals("")) {\r
-                       return;\r
-               }\r
-               \r
-               OAuthTokenizer tokenizer = new OAuthTokenizer();\r
-       AESEncrypter aes = new AESEncrypter();\r
-       try {\r
-                       aes.decrypt(new FileInputStream(Global.getFileManager().getHomeDirFile("oauth.txt")));\r
-               } catch (FileNotFoundException e) {\r
-                       e.printStackTrace();\r
-               }\r
-               String authString = aes.getString();\r
-               String oauthToken = new String();\r
-               if (!authString.equals("")) {\r
-                       tokenizer.tokenize(authString);\r
-                       oauthToken = tokenizer.oauth_token;\r
-               }\r
-               \r
-               HttpClient httpClient = new DefaultHttpClient();\r
-\r
-               HttpPost httpPost = new HttpPost("https://" + serverUrl + "/shard/" + user.getShardId() + "/thm/note/" + guid);\r
-               httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");\r
-               httpPost.setHeader("Host", serverUrl);\r
-\r
-               List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);\r
-               nameValuePairs.add(new BasicNameValuePair("auth", oauthToken));\r
-               nameValuePairs.add(new BasicNameValuePair("size", "80"));\r
-               \r
-               try {\r
-                       httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));\r
-                       // Webサーバからのレスポンスを処理\r
-                       HttpResponse response = null;\r
-                       response = httpClient.execute(httpPost);\r
-                       byte[] bytes = EntityUtils.toByteArray(response.getEntity());\r
-                       QByteArray data = new QByteArray(bytes);\r
-                       // データベースにEvernoteサーバから取得したサムネイルを保存。例↓\r
-                       // conn.getNoteTable().setThumbnail(guid, data);\r
-               } catch (UnsupportedEncodingException e) {\r
-                       e.printStackTrace();\r
-               } catch (ClientProtocolException e) {\r
-                       e.printStackTrace();\r
-               } catch (IOException e) {\r
-                       e.printStackTrace();\r
-               }\r
-\r
-               httpClient.getConnectionManager().shutdown();\r
        }\r
 }\r
diff --git a/src/cx/fbn/nevernote/signals/ENThumbnailSignal.java b/src/cx/fbn/nevernote/signals/ENThumbnailSignal.java
new file mode 100644 (file)
index 0000000..1653ca8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * This file is part of NeighborNote
+ * Copyright 2013 Yuki Takahashi
+ * 
+ * This file may be licensed under the terms of of the
+ * GNU General Public License Version 2 (the ``GPL'').
+ *
+ * Software distributed under the License is distributed
+ * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the GPL for the specific language
+ * governing rights and limitations.
+ *
+ * You should have received a copy of the GPL along with this
+ * program. If not, go to http://www.gnu.org/licenses/gpl.html
+ * or write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+*/
+
+package cx.fbn.nevernote.signals;
+
+import com.trolltech.qt.QSignalEmitter;
+
+public class ENThumbnailSignal extends QSignalEmitter {
+       public Signal1<String> getENThumbnailFinished = new Signal1<String>();
+}
index abc6c6d..8c0cee8 100644 (file)
@@ -159,7 +159,7 @@ public class ENRelatedNotesRunner extends QObject implements Runnable{
                this.keepRunning = keepRunning;
        }
        
-       public synchronized boolean addGuid(String guid) {
+       public boolean addGuid(String guid) {
                if (workQueue.offer("GET " + guid)) {
                        return true;
                }
@@ -167,7 +167,7 @@ public class ENRelatedNotesRunner extends QObject implements Runnable{
                return false;
        }
        
-       public synchronized boolean addStop() {
+       public boolean addStop() {
                if (workQueue.offer("STOP")) {
                        return true;
                }
diff --git a/src/cx/fbn/nevernote/threads/ENThumbnailRunner.java b/src/cx/fbn/nevernote/threads/ENThumbnailRunner.java
new file mode 100644 (file)
index 0000000..be84c90
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of NeighborNote
+ * Copyright 2013 Yuki Takahashi
+ * 
+ * This file may be licensed under the terms of of the
+ * GNU General Public License Version 2 (the ``GPL'').
+ *
+ * Software distributed under the License is distributed
+ * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the GPL for the specific language
+ * governing rights and limitations.
+ *
+ * You should have received a copy of the GPL along with this
+ * program. If not, go to http://www.gnu.org/licenses/gpl.html
+ * or write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+*/
+
+package cx.fbn.nevernote.threads;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import com.evernote.edam.type.User;
+import com.trolltech.qt.core.QByteArray;
+import com.trolltech.qt.core.QMutex;
+import com.trolltech.qt.core.QObject;
+import com.trolltech.qt.gui.QPixmap;
+
+import cx.fbn.nevernote.Global;
+import cx.fbn.nevernote.oauth.OAuthTokenizer;
+import cx.fbn.nevernote.signals.ENThumbnailSignal;
+import cx.fbn.nevernote.signals.LimitSignal;
+import cx.fbn.nevernote.utilities.AESEncrypter;
+import cx.fbn.nevernote.utilities.ApplicationLogger;
+
+public class ENThumbnailRunner extends QObject implements Runnable{
+       
+       private final ApplicationLogger                                 logger;
+       public volatile ENThumbnailSignal                               enThumbnailSignal;
+       public QMutex                                                                   mutex;
+       private volatile boolean                                                keepRunning;
+       private volatile LinkedBlockingQueue<String>    workQueue;
+       public volatile LimitSignal                                     limitSignal;
+       private volatile User                                                   user;
+       private volatile String                                                 serverUrl;
+       
+       public ENThumbnailRunner(ApplicationLogger logger) {
+               this.logger = logger;
+               this.enThumbnailSignal = new ENThumbnailSignal();
+               this.mutex = new QMutex();
+               this.keepRunning = true;
+               this.workQueue = new LinkedBlockingQueue<String>();
+               this.limitSignal = new LimitSignal();
+               this.user = new User();
+               this.serverUrl = "";
+       }
+
+       @Override
+       public void run() {
+               thread().setPriority(Thread.MIN_PRIORITY);
+               
+               logger.log(logger.MEDIUM, "ENThumbnailスレッド開始");
+               while (keepRunning) {
+                       try {
+                               String work = workQueue.take();
+                               mutex.lock();
+                               if (work.startsWith("GET")) {
+                                       String guid = work.replace("GET ", "");
+                                       logger.log(logger.EXTREME, "Evernoteサムネイル取得開始 guid = " + guid);
+                                       
+                                       QPixmap thumbnail_p = getENThumbnail(guid);
+                                       if (thumbnail_p == null) {                              // 取得に失敗
+                                               logger.log(logger.EXTREME, "Evernoteサムネイルの取得に失敗");
+                                       } else {
+                                               logger.log(logger.EXTREME, "Evernoteサムネイルの取得に成功");
+                                               saveImage(thumbnail_p, guid);
+                                       }
+                                       
+                                       enThumbnailSignal.getENThumbnailFinished.emit(guid);
+                                       logger.log(logger.EXTREME, "Evernoteサムネイル取得完了 guid = " + guid);
+                               } else if (work.startsWith("STOP")) {
+                                       logger.log(logger.MEDIUM, "ENThumbnailスレッド停止");
+                                       keepRunning = false;
+                               }
+                               mutex.unlock();
+                       } catch (InterruptedException e) {
+                               // TODO 自動生成された catch ブロック
+                               e.printStackTrace();
+                       }
+               }
+       }
+       
+       // Evernoteサーバからサムネイルを取得
+       private synchronized QPixmap getENThumbnail(String guid) {
+               // サムネイルをEvernoteサーバから取得
+               String shardId = user.getShardId();
+               if (shardId == null || shardId.equals("")) {
+                       return null;
+               }
+               
+               OAuthTokenizer tokenizer = new OAuthTokenizer();
+       AESEncrypter aes = new AESEncrypter();
+       try {
+                       aes.decrypt(new FileInputStream(Global.getFileManager().getHomeDirFile("oauth.txt")));
+               } catch (FileNotFoundException e) {
+                       e.printStackTrace();
+               }
+               String authString = aes.getString();
+               String oauthToken = new String();
+               if (!authString.equals("")) {
+                       tokenizer.tokenize(authString);
+                       oauthToken = tokenizer.oauth_token;
+               }
+               
+               HttpClient httpClient = new DefaultHttpClient();
+
+               HttpPost httpPost = new HttpPost("https://" + serverUrl + "/shard/" + user.getShardId() + "/thm/note/" + guid + ".png");
+               httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
+               httpPost.setHeader("Host", getServerUrl());
+
+               List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
+               nameValuePairs.add(new BasicNameValuePair("auth", oauthToken));
+               nameValuePairs.add(new BasicNameValuePair("size", "80"));
+               
+               QPixmap p = new QPixmap();
+               try {
+                       httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
+                       // Webサーバからのレスポンスを処理
+                       HttpResponse response = null;
+                       response = httpClient.execute(httpPost);
+                       byte[] bytes = EntityUtils.toByteArray(response.getEntity());
+                       QByteArray data = new QByteArray(bytes);
+                       // データベースにEvernoteサーバから取得したサムネイルを保存。例↓
+                       // conn.getNoteTable().setThumbnail(guid, data);
+                       
+                       p.loadFromData(data);
+               } catch (UnsupportedEncodingException e) {
+                       e.printStackTrace();
+                       return null;
+               } catch (ClientProtocolException e) {
+                       e.printStackTrace();
+                       return null;
+               } catch (IOException e) {
+                       e.printStackTrace();
+                       return null;
+               } finally {
+                       httpClient.getConnectionManager().shutdown();
+               }
+
+               return p;
+       }
+       
+       private synchronized void saveImage(QPixmap thumbnail, String guid) {
+               String thumbnailName = Global.getFileManager().getResDirPath("enThumbnail-" + guid + ".png");
+               thumbnail.save(thumbnailName, "PNG");
+       }
+
+       public boolean isKeepRunning() {
+               return keepRunning;
+       }
+
+       public void setKeepRunning(boolean keepRunning) {
+               this.keepRunning = keepRunning;
+       }
+       
+       public boolean addGuid(String guid) {
+               if (workQueue.offer("GET " + guid)) {
+                       return true;
+               }
+               
+               return false;
+       }
+       
+       public boolean addStop() {
+               if (workQueue.offer("STOP")) {
+                       return true;
+               }
+               return false;
+       }
+
+       public User getUser() {
+               return user;
+       }
+
+       public void setUser(User user) {
+               this.user = user;
+       }
+
+       public String getServerUrl() {
+               return serverUrl;
+       }
+
+       public void setServerUrl(String serverUrl) {
+               this.serverUrl = serverUrl;
+       }
+}