Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
Software
Programování na Macu - Aplikace pro kontrolu nové pošty (4)
30. dubna 2008, 09.00 | V tomto díle naimplementujeme ukládání nastavení a především stahování pošty z
mailového serveru.
Ukládání nastavení
V minulém díle jsme si ukázali, jak načíst nastavení z databáze. Ještě musíme propojit objekt Preferences s příslušným oknem a umožnit uživateli ukládání hodnot.Nejprve otevřete Interface Builder a zrušte zaškrtnutí "Visible at launch time". Okno se totiž nemá zobrazit při spuštění programu, ale pouze, pokud uživatel klikne na položku menu, kterou teď přidáme.
- (NSMenu*)createMenu {
NSMenu* menu = [[NSMenu alloc] initWithTitle: @"Email Notifier"];
NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"Info..." action: @selector(about) keyEquivalent: @""];
[item setTarget: self];
[menu addItem: item];
[menu addItem: [NSMenuItem separatorItem]];
item = [[NSMenuItem alloc] initWithTitle: @"Preferences..." action: @selector(openPrefs) keyEquivalent: @""];
[item setTarget: self];
[menu addItem: item];
[menu addItem: [NSMenuItem separatorItem]];
item = [[NSMenuItem alloc] initWithTitle: @"Quit" action: @selector(exit) keyEquivalent: @""];
[item setTarget: self];
[menu addItem: item];
return menu;
}
- (void)openPrefs {
[prefsPanel makeKeyAndOrderFront: nil];
}
Přeložte a spusťte program. Po spuštění se okno neukáže jako dříve, ale až po kliknutí na položku "Preferences..." v menu.
A teď k vlastnímu propojení s poli v okně. Do metody openPrefs přidejte:
- (void)openPrefs {
[userName setStringValue: [javaController userName]];
[password setStringValue: [javaController password]];
[NSApp activateIgnoringOtherApps: YES];
[prefsPanel makeKeyAndOrderFront: nil];
}
Tímto kódem nastavíme obsah obou textových polí. Třetí řádek aktivuje okno, aby přijímalo zprávy týkající se klávesnice. Zároveň musíme upravit definici rozhraní do Javy, tj. soubor JavaInterfaces.h:
@interface Test : NSObject
{}
- (NSString*)userName;
- (NSString*)password;
- (void)setUserName:(NSString*)userName;
- (void)setPassword:(NSString*)password;
- (void)save;
@end
A nakonec rozšíříme třídu Test.java o následující metody:
public void save() {
db.set(prefs);
db.commit();
}
public String userName() {
return prefs.getUserName() != null ? prefs.getUserName() : "";
}
public String password() {
return prefs.getPassword() != null ? prefs.getPassword() : "";
}
public void setUserName(String s) {
prefs.setUserName(s);
}
public void setPassword(String s) {
prefs.setPassword(s);
}
Periodická kontrola pošty
A nyní již k vlastní kontrole pošty. Do souboru AppController.m přidejte metodu checkMail:- (void)checkMail {
[javaController checkMail: statusItem];
NSMethodSignature* sig = [self methodSignatureForSelector: @selector(checkMail)];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: sig];
[invocation setTarget: self];
[invocation setSelector: @selector(checkMail)];
[NSTimer scheduledTimerWithTimeInterval: 60 invocation: invocation repeats: NO];
}
Nezapomeňte přidat její deklaraci do rozhraní v souboru AppController.h. Do metody awakeFromNib přidejte nakonec iniciální zavolání této metody ([self checkMail]).
Do třídy Test přidáme rovněž metodu checkMail. Nezapomeňte přidat její deklaraci také do JavaInterfaces.h:
import com.apple.cocoa.application.*;
public class Test {
public void checkMail(NSStatusItem statusItem) {
int msgCount = new MailCheck(prefs, db).check();
statusItem.setTitle(msgCount == 0 ? "" : String.valueOf(msgCount));
}
...
Nezapomeňte na direktivu import, aby překladač neohlásil chybu kvůli neznámé třídě NSStatusItem. Z kódu je zřejmé, že klíčová funkčnost je skryta ve třídě MailCheck, jejíž metoda check vrátí počet nových zpráv. Tento počet zobrazíme v liště vedle ikony, nebo se nezobrazí žádný text, pokud žádné nové zprávy ve schránce nejsou.
Pro testování nejprve použijeme pouze kostru třídy MailCheck:
import com.db4o.*;
public class MailCheck {
protected Preferences prefs;
protected ObjectContainer db;
public MailCheck(Preferences p, ObjectContainer db) {
prefs = p;
this.db = db;
}
public int check() {
return 1;
}
}
Po spuštění by se vám měla vedle ikonky zobrazit jednička, kterou nyní máme v kódu "natvrdo". Funguje? V tom případě použijte následující definici metody check:
public int check() {
int msgCount = 0;
try {
Properties props = new Properties();
props.setProperty("mail.pop3.host", prefs.getHost());
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(prefs.getUserName(), prefs.getPassword());
}
});
session.setDebug(true);
Store store = session.getStore("pop3");
store.connect();
Folder folder = store.getDefaultFolder();
folder = folder.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
int count = folder.getMessageCount();
Message[] messages = folder.getMessages(0, count);
try {
for (int i = 0; i < messages.length; i++) {
Message msg = messages[i];
try {
final Mail mail = new Mail();
mail.init(Arrays.toString(msg.getFrom()), msg.getSubject(), msg.getSentDate());
List<Mail> mails = db.query(new Predicate<Mail>() {
public boolean match(Mail m) {
return mail.getSignature().equals(m.getSignature());
}
});
if (mails.size() == 0) {
msgCount++;
System.out.println(msg.getFrom()[0] + " - " + msg.getSubject());
db.set(mail);
}
} catch (Exception e) { e.printStackTrace(); }
}
System.out.println("checked: " + msgCount);
db.commit();
} catch (Exception e) {
db.rollback();
e.printStackTrace();
}
if (folder.isOpen()) folder.close(true);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
return msgCount;
}
Aby šel kód přeložit, přidejte do projektu soubory activation.jar, mailapi.jar a pop3.jar (lze stáhnout ze stránek společnosti Sun). Zároveň rozšiřte soubor Info.plist:
<key>NSJavaPath</key>
<array>
<string>JavaClasses.jar</string>
<string>db4o.jar</string>
<string>activation.jar</string>
<string>mailapi.jar</string>
<string>pop3.jar</string>
</array>
Ještě chybí třída Mail.java:
import java.util.*;
public class Mail {
private String signature;
public void init(String from, String subject, Date date) {
signature = from + ":" + subject + ":" + date;
}
public String getSignature() {
return signature;
}
}
Hlavním tématem této série článků je tvorba aplikací pro OS X s aplikačním rozhraním Cocoa, proto kód pro kontrolu pošty projdeme pouze stručně. Nejdříve se připojíme k emailové schránce. Potom získáme instanci složky Inbox a projdeme všechny zprávy v ní obsažené. Protokol POP3 bohužel neumožňuje předání informací o tom, je-li zpráva nová, či nepřečtená, to si musíme zjistit sami. Proto si každou "viděnou" zprávu poznamenáme v databázi. Je-li ve složce Inbox zpráva, o které ještě nevíme (není v databázi), považujeme ji za novou a zvýšíme proměnnou msgCount o jedničku.
K ukládání informací o známých zprávách slouží třída Mail. Stojí za to poznamenat, že při hledání v databázi nedochází vždy k procházení všech záznamů (to by bylo ostatně dost neefektivní), ale díky rozhraní Predicate můžeme nadefinovat nativní dotaz do databáze, který je před provedením automaticky optimalizován a použit k vyhledání záznamu pomocí indexu objektů.
Aplikace má ještě jednu drobnou vadu. Jistě jste si všimli, zvláště pokud máte pomalejší připojení, že během kontrolování pošty náš program "zamrzne". Konkrétně nejde vyvolat menu kliknutím na ikonku. Ideální by bylo provádět kontrolu na pozadí. Přidáme proto do třídy MailCheck tuto metodu:
public void checkInBackground(final AsyncCallback callback) {
new Thread(new Runnable() {
public void run() {
int msgCount = check();
callback.finished(new Integer(msgCount));
}
}).start();
}
Tato metoda vytvoří nové vlákno a teprve v něm zavolá metodu check. Výsledek je vrácen přes parametr callback, který je instancí anonymní třídy implementující následující rozhraní:
public interface AsyncCallback {
public void finished(Object result);
}
Ve třídě Test nyní metoda checkMail vypadá takto:
public void checkMail(final NSStatusItem statusItem) {
new MailCheck(prefs, db).checkInBackground(new AsyncCallback() {
public void finished(Object result) {
int msgCount = ((Integer) result).intValue();
statusItem.setTitle(msgCount == 0 ? "" : String.valueOf(msgCount));
if (msgCount > 0) NSSound.soundNamed("Glass").play();
}
});
}
Tím máme aplikace v podstatě dokončenou. Je to samozřejmě pouze základ, určitě by se dalo mnoho věcí vylepšit a ne vše je vyřešeno optimálně, ale další drobné úpravy už určitě zvládnete sami.
Jedna věc ale přeci jen chybí – v prvním článku jsme si slíbili, že aplikace bude umět fulltextové vyhledávání. V dalším a posledním díle tohoto seriálu se dozvíte, jak na to.
Obsah seriálu (více o seriálu):
- Programování na Macu - Aplikace pro kontrolu nové pošty (1)
- Programování na Macu - Aplikace pro kontrolu nové pošty (2)
- Programování na Macu - Aplikace pro kontrolu nové pošty (3)
- Programování na Macu - Aplikace pro kontrolu nové pošty (4)
- Programování na Macu - Aplikace pro kontrolu nové pošty (5)