refactor, use greendao and okhttp, and some new things

changed things
- use greenDAO instead of custom SQLiteOpenHelper thing
- use OkHttp for all HTTP comunication
- extract preferences to Settings
- put share stuff in BagItProxyActivity
- devide things in packages
- new archive icon

new things
- create WallabagService and start moving all comunication there
- require username and password, and add links directly, not bounce to
browser
- make archive actually archive things on the server
This commit is contained in:
Victor Häggqvist
2015-10-20 21:14:26 +02:00
parent fadea813e2
commit 3dd80c032c
47 changed files with 1981 additions and 1118 deletions
+15
View File
@@ -11,15 +11,30 @@ android {
versionCode 11
versionName "1.8"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
java {
srcDir 'src/main/java'
srcDir 'src-gen'
}
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:22.+'
compile 'com.android.support:recyclerview-v7:22.+'
compile 'com.android.support:design:22.+'
compile 'de.greenrobot:greendao:2.0.0'
compile 'com.facebook.stetho:stetho:1.2.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.facebook.stetho:stetho-okhttp:1.2.0'
}
@@ -0,0 +1,123 @@
package fr.gaulupeau.apps.Poche.entity;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit.
/**
* Entity mapped to table "ARTICLE".
*/
public class Article {
private Long id;
private Integer articleId;
private String content;
private String author;
private String title;
private String url;
private Boolean archive;
private Boolean sync;
private java.util.Date updateDate;
public Article() {
}
public Article(Long id) {
this.id = id;
}
public Article(Long id, Integer articleId, String content, String author, String title, String url, Boolean archive, Boolean sync, java.util.Date updateDate) {
this.id = id;
this.articleId = articleId;
this.content = content;
this.author = author;
this.title = title;
this.url = url;
this.archive = archive;
this.sync = sync;
this.updateDate = updateDate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getArticleId() {
return articleId;
}
public void setArticleId(Integer articleId) {
this.articleId = articleId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Boolean getArchive() {
return archive;
}
public void setArchive(Boolean archive) {
this.archive = archive;
}
public Boolean getSync() {
return sync;
}
public void setSync(Boolean sync) {
this.sync = sync;
}
public java.util.Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(java.util.Date updateDate) {
this.updateDate = updateDate;
}
@Override
public String toString() {
return "Article{" +
"id=" + id +
", articleId=" + articleId +
", author='" + author + '\'' +
", title='" + title + '\'' +
", url='" + url + '\'' +
", archive=" + archive +
", sync=" + sync +
", updateDate=" + updateDate +
'}';
}
}
@@ -0,0 +1,178 @@
package fr.gaulupeau.apps.Poche.entity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property;
import de.greenrobot.dao.internal.DaoConfig;
import fr.gaulupeau.apps.Poche.entity.Article;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* DAO for table "ARTICLE".
*/
public class ArticleDao extends AbstractDao<Article, Long> {
public static final String TABLENAME = "ARTICLE";
/**
* Properties of entity Article.<br/>
* Can be used for QueryBuilder and for referencing column names.
*/
public static class Properties {
public final static Property Id = new Property(0, Long.class, "id", true, "_id");
public final static Property ArticleId = new Property(1, Integer.class, "articleId", false, "article_id");
public final static Property Content = new Property(2, String.class, "content", false, "content");
public final static Property Author = new Property(3, String.class, "author", false, "author");
public final static Property Title = new Property(4, String.class, "title", false, "title");
public final static Property Url = new Property(5, String.class, "url", false, "url");
public final static Property Archive = new Property(6, Boolean.class, "archive", false, "archive");
public final static Property Sync = new Property(7, Boolean.class, "sync", false, "sync");
public final static Property UpdateDate = new Property(8, java.util.Date.class, "updateDate", false, "update_date");
};
public ArticleDao(DaoConfig config) {
super(config);
}
public ArticleDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
}
/** Creates the underlying database table. */
public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"ARTICLE\" (" + //
"\"_id\" INTEGER PRIMARY KEY ," + // 0: id
"\"article_id\" INTEGER UNIQUE ," + // 1: articleId
"\"content\" TEXT," + // 2: content
"\"author\" TEXT," + // 3: author
"\"title\" TEXT," + // 4: title
"\"url\" TEXT," + // 5: url
"\"archive\" INTEGER," + // 6: archive
"\"sync\" INTEGER," + // 7: sync
"\"update_date\" INTEGER);"); // 8: updateDate
}
/** Drops the underlying database table. */
public static void dropTable(SQLiteDatabase db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"ARTICLE\"";
db.execSQL(sql);
}
/** @inheritdoc */
@Override
protected void bindValues(SQLiteStatement stmt, Article entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
Integer articleId = entity.getArticleId();
if (articleId != null) {
stmt.bindLong(2, articleId);
}
String content = entity.getContent();
if (content != null) {
stmt.bindString(3, content);
}
String author = entity.getAuthor();
if (author != null) {
stmt.bindString(4, author);
}
String title = entity.getTitle();
if (title != null) {
stmt.bindString(5, title);
}
String url = entity.getUrl();
if (url != null) {
stmt.bindString(6, url);
}
Boolean archive = entity.getArchive();
if (archive != null) {
stmt.bindLong(7, archive ? 1L: 0L);
}
Boolean sync = entity.getSync();
if (sync != null) {
stmt.bindLong(8, sync ? 1L: 0L);
}
java.util.Date updateDate = entity.getUpdateDate();
if (updateDate != null) {
stmt.bindLong(9, updateDate.getTime());
}
}
/** @inheritdoc */
@Override
public Long readKey(Cursor cursor, int offset) {
return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
}
/** @inheritdoc */
@Override
public Article readEntity(Cursor cursor, int offset) {
Article entity = new Article( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.isNull(offset + 1) ? null : cursor.getInt(offset + 1), // articleId
cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // content
cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // author
cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // title
cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // url
cursor.isNull(offset + 6) ? null : cursor.getShort(offset + 6) != 0, // archive
cursor.isNull(offset + 7) ? null : cursor.getShort(offset + 7) != 0, // sync
cursor.isNull(offset + 8) ? null : new java.util.Date(cursor.getLong(offset + 8)) // updateDate
);
return entity;
}
/** @inheritdoc */
@Override
public void readEntity(Cursor cursor, Article entity, int offset) {
entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
entity.setArticleId(cursor.isNull(offset + 1) ? null : cursor.getInt(offset + 1));
entity.setContent(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
entity.setAuthor(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));
entity.setTitle(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));
entity.setUrl(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5));
entity.setArchive(cursor.isNull(offset + 6) ? null : cursor.getShort(offset + 6) != 0);
entity.setSync(cursor.isNull(offset + 7) ? null : cursor.getShort(offset + 7) != 0);
entity.setUpdateDate(cursor.isNull(offset + 8) ? null : new java.util.Date(cursor.getLong(offset + 8)));
}
/** @inheritdoc */
@Override
protected Long updateKeyAfterInsert(Article entity, long rowId) {
entity.setId(rowId);
return rowId;
}
/** @inheritdoc */
@Override
public Long getKey(Article entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
/** @inheritdoc */
@Override
protected boolean isEntityUpdateable() {
return true;
}
}
@@ -0,0 +1,70 @@
package fr.gaulupeau.apps.Poche.entity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import de.greenrobot.dao.AbstractDaoMaster;
import de.greenrobot.dao.identityscope.IdentityScopeType;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* Master of DAO (schema version 1): knows all DAOs.
*/
public class DaoMaster extends AbstractDaoMaster {
public static final int SCHEMA_VERSION = 1;
/** Creates underlying database table using DAOs. */
public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {
ArticleDao.createTable(db, ifNotExists);
}
/** Drops underlying database table using DAOs. */
public static void dropAllTables(SQLiteDatabase db, boolean ifExists) {
ArticleDao.dropTable(db, ifExists);
}
public static abstract class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory, SCHEMA_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
createAllTables(db, false);
}
}
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}
public DaoMaster(SQLiteDatabase db) {
super(db, SCHEMA_VERSION);
registerDaoClass(ArticleDao.class);
}
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
public DaoSession newSession(IdentityScopeType type) {
return new DaoSession(db, type, daoConfigMap);
}
}
@@ -0,0 +1,49 @@
package fr.gaulupeau.apps.Poche.entity;
import android.database.sqlite.SQLiteDatabase;
import java.util.Map;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.AbstractDaoSession;
import de.greenrobot.dao.identityscope.IdentityScopeType;
import de.greenrobot.dao.internal.DaoConfig;
import fr.gaulupeau.apps.Poche.entity.Article;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* {@inheritDoc}
*
* @see de.greenrobot.dao.AbstractDaoSession
*/
public class DaoSession extends AbstractDaoSession {
private final DaoConfig articleDaoConfig;
private final ArticleDao articleDao;
public DaoSession(SQLiteDatabase db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
daoConfigMap) {
super(db);
articleDaoConfig = daoConfigMap.get(ArticleDao.class).clone();
articleDaoConfig.initIdentityScope(type);
articleDao = new ArticleDao(articleDaoConfig, this);
registerDao(Article.class, articleDao);
}
public void clear() {
articleDaoConfig.getIdentityScope().clear();
}
public ArticleDao getArticleDao() {
return articleDao;
}
}
+23 -7
View File
@@ -6,28 +6,44 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name="fr.gaulupeau.apps.Poche.App"
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/app_theme">
<activity
android:name="fr.gaulupeau.apps.Poche.Poche"
android:name="fr.gaulupeau.apps.Poche.ui.PocheActivity"
android:theme="@style/mainActivity_theme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!--<intent-filter android:label="@string/label_name">-->
<!--<action android:name="android.intent.action.SEND" />-->
<!--<category android:name="android.intent.category.DEFAULT" />-->
<!--<data android:mimeType="text/plain" />-->
<!--</intent-filter>-->
</activity>
<activity android:name="fr.gaulupeau.apps.Poche.ui.ReadArticleActivity" />
<activity android:name="fr.gaulupeau.apps.Poche.ui.ListArticlesActivity" />
<activity
android:name="fr.gaulupeau.apps.Poche.ui.SettingsActivity"
android:label="@string/btnSettings" />
<activity
android:name="fr.gaulupeau.apps.Poche.ui.BagItProxyActivity"
android:theme="@style/ProxyTheme">
<intent-filter android:label="@string/label_name">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity android:name="fr.gaulupeau.apps.Poche.ReadArticle" />
<activity android:name="fr.gaulupeau.apps.Poche.ListArticles" />
<activity
android:name="fr.gaulupeau.apps.Poche.Settings"
android:label="@string/btnSettings" />
<activity android:name="fr.gaulupeau.apps.Poche.ui.AddActivity"></activity>
</application>
</manifest>
@@ -0,0 +1,31 @@
package fr.gaulupeau.apps.Poche;
import android.app.Application;
import com.facebook.stetho.Stetho;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
import fr.gaulupeau.apps.Poche.data.DbConnection;
import fr.gaulupeau.apps.Poche.data.Settings;
/**
* @author Victor Häggqvist
* @since 10/19/15
*/
public class App extends Application {
private Settings settings;
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG)
Stetho.initializeWithDefaults(this);
DbConnection.setContext(this);
settings = new Settings(this);
}
public Settings getSettings() {
return settings;
}
}
@@ -1,35 +0,0 @@
package fr.gaulupeau.apps.Poche;
import java.net.URL;
public class Article {
public String url;
public String id;
public String title;
public String content;
public String archive;
private URL m_url = null;
public Article(String url, String id, String title, String content, String archive) {
super();
this.url = url;
this.id = id;
this.title = title;
this.content = content;
this.archive = archive;
try {
this.m_url = new URL(url);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getHostOfUrl() {
if (this.m_url != null) {
return m_url.getHost();
}
return "";
}
}
@@ -1,79 +0,0 @@
package fr.gaulupeau.apps.Poche;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import static fr.gaulupeau.apps.Poche.Helpers.PREFS_NAME;
import static fr.gaulupeau.apps.Poche.Helpers.zeroUpdate;
public class ArticlesSQLiteOpenHelper extends SQLiteOpenHelper {
public static final int VERSION = 1;
public static final String DB_NAME = "article_db.sqlite";
public static String MY_ID = "my_id";
public static String ARTICLE_TABLE = "article";
public static String ARTICLE_DATE = "update_date";
public static String ARTICLE_ID = "article_id";
public static String ARTICLE_AUTHOR = "author";
public static String ARTICLE_CONTENT = "content";
public static String ARTICLE_TITLE = "title";
public static String ARTICLE_URL = "url";
public static String ARCHIVE = "archive";
public static String ARTICLE_SYNC = "sync";
public static String ARTICLE_READAT = "read_at";
Context c;
public ArticlesSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, VERSION);
c = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
createTables(db);
}
@Override
public void onOpen(SQLiteDatabase db) {
// TODO Auto-generated method stub
super.onOpen(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.delete(ARTICLE_TABLE, null, null);
SharedPreferences preferences = c.getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("previous_update", zeroUpdate);
editor.commit();
}
protected void createTables(SQLiteDatabase db) {
db.execSQL(
"create table " + ARTICLE_TABLE + " (" +
MY_ID + " integer primary key autoincrement not null, " +
ARTICLE_AUTHOR + " text, " +
ARTICLE_DATE + " datetime, " +
ARTICLE_CONTENT + " text, " +
ARTICLE_TITLE + " text, " +
ARTICLE_URL + " text, " +
ARTICLE_ID + " integer, " +
ARCHIVE + " integer," +
ARTICLE_SYNC + " integer," +
ARTICLE_READAT + " integer," +
"UNIQUE (" + ARTICLE_URL + ")" +
");"
);
}
public void truncateTables(SQLiteDatabase db) {
db.execSQL("DELETE FROM " + ARTICLE_TABLE + ";");
}
}
@@ -1,278 +0,0 @@
package fr.gaulupeau.apps.Poche;
import android.content.ContentValues;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.AsyncTask;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import fr.gaulupeau.apps.InThePoche.R;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARCHIVE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_CONTENT;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_DATE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_SYNC;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TABLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TITLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_URL;
/**
* Created by kevinmeyer on 13/12/14.
*/
interface FeedUpdaterInterface {
void feedUpdaterFinishedWithError(String errorMessage);
void feedUpdatedFinishedSuccessfully();
}
public class FeedUpdater extends AsyncTask<Void, Void, Void> {
private SQLiteDatabase database;
private String wallabagUrl;
private String apiUserId;
private String apiToken;
private FeedUpdaterInterface callback;
private String errorMessage;
public FeedUpdater(String wallabagUrl, String apiUserId, String apiToken, SQLiteDatabase writableDatabase, FeedUpdaterInterface callback) {
this.wallabagUrl = wallabagUrl;
this.apiUserId = apiUserId;
this.apiToken = apiToken;
this.database = writableDatabase;
this.callback = callback;
}
@Override
protected Void doInBackground(Void... params) {
parseRSS();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (callback == null)
return;
if (errorMessage == null) {
callback.feedUpdatedFinishedSuccessfully();
} else {
callback.feedUpdaterFinishedWithError(errorMessage);
}
}
private void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
public void parseRSS() {
URL url;
try {
// Set the url (you will need to change this to your RSS URL
url = new URL(wallabagUrl + "/?feed&type=home&user_id=" + apiUserId + "&token=" + apiToken);
if (wallabagUrl.startsWith("https")) {
trustEveryone();
}
// Setup the connection
HttpURLConnection urlConnection;
urlConnection = (HttpURLConnection) url.openConnection();
if ((urlConnection != null) && (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK)) {
// Retreive the XML from the URL
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = null;
InputSource is;
try {
is = new InputSource(
new InputStreamReader(
urlConnection.getInputStream()));
doc = db.parse(is);
doc.getDocumentElement().normalize();
} catch (SAXException e) {
e.printStackTrace();
InputStream inputStream = url.openStream();
int ch;
StringBuffer stringBuffer = new StringBuffer();
while ((ch = inputStream.read()) != -1) {
stringBuffer.append((char) ch);
}
errorMessage = ":\nGot invalid response:\n\"" + stringBuffer.toString() + "\"";
return;
}
// This is the root node of each section you want to parse
NodeList itemLst = doc.getElementsByTagName("item");
// This sets up some arrays to hold the data parsed
arrays.PodcastTitle = new String[itemLst.getLength()];
arrays.PodcastURL = new String[itemLst.getLength()];
arrays.PodcastContent = new String[itemLst.getLength()];
arrays.PodcastMedia = new String[itemLst.getLength()];
arrays.PodcastDate = new String[itemLst.getLength()];
// Loop through the XML passing the data to the arrays
for (int i = 0; i < itemLst.getLength(); i++) {
Node item = itemLst.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE) {
Element ielem = (Element) item;
// This section gets the elements from the XML
// that we want to use you will need to add
// and remove elements that you want / don't want
NodeList title = ielem.getElementsByTagName("title");
NodeList link = ielem.getElementsByTagName("link");
NodeList date = ielem.getElementsByTagName("pubDate");
NodeList content = ielem
.getElementsByTagName("description");
//NodeList media = ielem
// .getElementsByTagName("media:content");
// This is an attribute of an element so I create
// a string to make it easier to use
//String mediaurl = media.item(0).getAttributes()
// .getNamedItem("url").getNodeValue();
// This section adds an entry to the arrays with the
// data retrieved from above. I have surrounded each
// with try/catch just incase the element does not
// exist
try {
arrays.PodcastTitle[i] = cleanString(title.item(0).getChildNodes().item(0).getNodeValue());
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastTitle[i] = "Echec";
}
try {
arrays.PodcastDate[i] = date.item(0).getChildNodes().item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastDate[i] = null;
}
try {
arrays.PodcastURL[i] = link.item(0).getChildNodes()
.item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastURL[i] = "Echec";
}
try {
arrays.PodcastContent[i] = content.item(0)
.getChildNodes().item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastContent[i] = "Echec";
}
ContentValues values = new ContentValues();
values.put(ARTICLE_TITLE, arrays.PodcastTitle[i]);
values.put(ARTICLE_CONTENT, arrays.PodcastContent[i]);
//values.put(ARTICLE_ID, Html.fromHtml(article.getString("id")).toString());
values.put(ARTICLE_URL, arrays.PodcastURL[i]);
values.put(ARTICLE_DATE, arrays.PodcastDate[i]);
values.put(ARCHIVE, 0);
values.put(ARTICLE_SYNC, 0);
try {
database.insertOrThrow(ARTICLE_TABLE, null, values);
} catch (SQLiteConstraintException e) {
continue;
} catch (SQLiteException e) {
database.execSQL("ALTER TABLE " + ARTICLE_TABLE + " ADD COLUMN " + ARTICLE_DATE + " datetime;");
database.insertOrThrow(ARTICLE_TABLE, null, values);
}
}
}
} else {
// HTTP Connection not successful
if (urlConnection == null) {
errorMessage = "";
} else {
errorMessage = ":\n" + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String cleanString(String s) {
s = s.replace("&Atilde;&copy;", "&eacute;");
s = s.replace("&Atilde;&uml;", "&egrave;");
s = s.replace("&Atilde;&ordf;", "&ecirc;");
s = s.replace("&Atilde;&laquo;", "&euml;");
s = s.replace("&Atilde;&nbsp;", "&agrave;");
s = s.replace("&Atilde;&curren;", "&auml;");
s = s.replace("&Atilde;&cent;", "&acirc;");
s = s.replace("&Atilde;&sup1;", "&ugrave;");
s = s.replace("&Atilde;&raquo;", "&ucirc;");
s = s.replace("&Atilde;&frac14;", "&uuml;");
s = s.replace("&Atilde;&acute;", "&ocirc;");
s = s.replace("&Atilde;&para;", "&ouml;");
s = s.replace("&Atilde;&reg;", "&icirc;");
s = s.replace("&Atilde;&macr;", "&iuml;");
s = s.replace("&Atilde;&sect;", "&ccedil;");
s = s.replace("&amp;", "&amp;");
// Replace multiple whitespaces with single space
s = s.replaceAll("\\s+", " ");
s = s.trim();
return s;
}
}
@@ -1,52 +0,0 @@
package fr.gaulupeau.apps.Poche;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Helpers {
public static final String PREFS_NAME = "InThePoche";
public final static String zeroUpdate = "2011-01-01 00:00:00";
public static String InputStreamtoString(InputStream is) {
String s = "", line = "";
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
try {
for (; ; rd.readLine()) {
if ((line = rd.readLine()) != null) {
s += line;
} else {
break;
}
}
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
return s;
}
public static String getInputStreamFromUrl(String url) {
InputStream content;
String res = "";
try {
HttpGet httpGet = new HttpGet(url);
HttpClient httpclient = new DefaultHttpClient();
// Execute HTTP Get Request
HttpResponse response = httpclient.execute(httpGet);
content = response.getEntity().getContent();
res = InputStreamtoString(content);
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
}
@@ -1,139 +0,0 @@
package fr.gaulupeau.apps.Poche;
import android.annotation.TargetApi;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import fr.gaulupeau.apps.InThePoche.R;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARCHIVE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_DATE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TABLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TITLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_URL;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.MY_ID;
public class ListArticles extends BaseActionBarActivity {
private SQLiteDatabase database;
private ListView readList;
private boolean showAll = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
readList = (ListView) findViewById(R.id.liste_articles);
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(this);
database = helper.getWritableDatabase();
updateList();
}
@Override
protected void onResume() {
super.onResume();
updateList();
}
public void onDestroy() {
super.onDestroy();
database.close();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_list, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.menuShowAll).setTitle(getString(showAll ? R.string.menuShowUnread : R.string.menuShowAll));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuShowAll:
showAll = !showAll;
updateList();
return true;
case R.id.menuWipeDb:
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(this);
helper.truncateTables(database);
updateList();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void updateList() {
CursorAdapter adapter = (CursorAdapter) readList.getAdapter();
if (adapter != null) {
adapter.changeCursor(getCursor());
} else {
setupListAdapter();
}
setTitle(getString(R.string.app_name) + " | " + getResources().getQuantityString(R.plurals.numberOfArticles, readList.getCount(), readList.getCount()));
}
private void setupListAdapter() {
CursorAdapter adapter = getCursorAdapter();
readList.setAdapter(adapter);
readList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i = new Intent(getBaseContext(), ReadArticle.class);
// As we use a CursorAdapter the id's are the same as in our SQLite Database.
i.putExtra("id", id);
startActivity(i);
}
});
}
private Cursor getCursor() {
String filter = null;
if (!showAll) {
filter = ARCHIVE + "=0";
}
// the " as _id" extension is important, as a CursorAdapter needs a column named '_id' to work
// with this extension we get something starting like "Select id as _id,"...
String[] columns = new String[]{MY_ID + " as _id", ARTICLE_TITLE, ARTICLE_URL};
return database.query(
ARTICLE_TABLE,
columns,
filter, null, null, null, ARTICLE_DATE + " DESC");
}
@TargetApi(11)
private CursorAdapter getCursorAdapter() {
int layout = R.layout.article_list;
String[] columns = new String[]{ARTICLE_TITLE, ARTICLE_URL};
int[] toIds = new int[]{R.id.listitem_titre, R.id.listitem_textview_url};
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return getCursorAdapterPreHoneycomb(layout, columns, toIds);
}
return new SimpleCursorAdapter(this, layout, getCursor(), columns, toIds, 0);
}
@TargetApi(8)
private CursorAdapter getCursorAdapterPreHoneycomb(int layout, String[] from, int[] to) {
return new SimpleCursorAdapter(this, layout, getCursor(), from, to);
}
}
@@ -1,311 +0,0 @@
/**
* Android to Poche
* A simple app to make the full save bookmark to Poche
* web page available via the Share menu on Android tablets
* @author GAULUPEAU Jonathan
* August 2013
*/
package fr.gaulupeau.apps.Poche;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Browser;
import android.util.Base64;
import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import fr.gaulupeau.apps.InThePoche.R;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARCHIVE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_CONTENT;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_DATE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_SYNC;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TABLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TITLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_URL;
import static fr.gaulupeau.apps.Poche.Helpers.PREFS_NAME;
/**
* Main activity class
*/
@TargetApi(Build.VERSION_CODES.FROYO)
public class Poche extends Activity implements FeedUpdaterInterface {
private static SQLiteDatabase database;
Button btnGetPost;
Button btnSync;
Button btnSettings;
SharedPreferences settings;
static String apiUsername;
static String apiToken;
static String pocheUrl;
String action;
private FeedUpdater feedUpdater;
/**
* Called when the activity is first created.
* Will act differently depending on whether sharing or
* displaying information page.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
action = intent.getAction();
getSettings();
// Find out if Sharing or if app has been launched from icon
if (action.equals(Intent.ACTION_SEND) && !pocheUrl.equals("http://")) {
setContentView(R.layout.main);
findViewById(R.id.btnSync).setVisibility(View.GONE);
findViewById(R.id.btnGetPost).setVisibility(View.GONE);
findViewById(R.id.progressBar1).setVisibility(View.VISIBLE);
final String extraText = extras.getString("android.intent.extra.TEXT");
final String pageUrl;
// Parsing string for urls.
Matcher matcher = Patterns.WEB_URL.matcher(extraText);
if (matcher.find()) {
pageUrl = matcher.group();
} else {
showErrorMessage("Couldn't find a URL in share string:\n" + extraText);
return;
}
// Vérification de la connectivité Internet
final ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isConnected()) {
// Start to build the poche URL
Uri.Builder pocheSaveUrl = Uri.parse(pocheUrl).buildUpon();
// Add the parameters from the call
pocheSaveUrl.appendQueryParameter("action", "add");
byte[] data = null;
try {
data = pageUrl.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
pocheSaveUrl.appendQueryParameter("url", base64);
System.out.println("base64 : " + base64);
System.out.println("pageurl : " + pageUrl);
// Load the constructed URL in the browser
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(pocheSaveUrl.build());
i.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName());
// If user has more then one browser installed give them a chance to
// select which one they want to use
startActivity(i);
// That is all this app needs to do, so call finish()
this.finish();
} else {
// Afficher alerte connectivité
showToast(getString(R.string.txtNetOffline));
}
} else {
setContentView(R.layout.main);
checkAndHandleAfterUpdate();
btnSync = (Button) findViewById(R.id.btnSync);
btnSync.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateFeed();
}
});
btnGetPost = (Button) findViewById(R.id.btnGetPost);
btnGetPost.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getBaseContext(), ListArticles.class));
}
});
btnSettings = (Button) findViewById(R.id.btnSettings);
btnSettings.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(getBaseContext(), Settings.class));
}
});
}
}
private void updateFeed() {
// Ensure Internet connectivity
final ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
if (pocheUrl.equals("http://")) {
showToast(getString(R.string.txtConfigNotSet));
} else if (activeNetwork != null && activeNetwork.isConnected()) {
// Run update task
findViewById(R.id.progressBar1).setVisibility(View.VISIBLE);
ArticlesSQLiteOpenHelper sqLiteOpenHelper = new ArticlesSQLiteOpenHelper(this);
feedUpdater = new FeedUpdater(pocheUrl, apiUsername, apiToken, sqLiteOpenHelper.getWritableDatabase(), this);
feedUpdater.execute();
} else {
// Show message if not connected
showToast(getString(R.string.txtNetOffline));
}
}
private void checkAndHandleAfterUpdate() {
SharedPreferences pref = getSharedPreferences(PREFS_NAME, 0);
if (pref.getInt("update_checker", 0) < 9) {
// Wipe Database, because we now save HTML content instead of plain text
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(this);
getDatabase();
helper.truncateTables(database);
showToast("Update: Wiped Database. Please synchronize.");
}
int versionCode;
try {
versionCode = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0).versionCode;
} catch (Exception e) {
versionCode = 0;
}
pref.edit().putInt("update_checker", versionCode).commit();
}
private void getSettings() {
settings = getSharedPreferences(PREFS_NAME, 0);
pocheUrl = settings.getString("pocheUrl", "http://");
apiUsername = settings.getString("APIUsername", "");
apiToken = settings.getString("APIToken", "");
}
private void getDatabase() {
if (database == null) {
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(this);
database = helper.getReadableDatabase();
}
}
@Override
protected void onResume() {
super.onResume();
getSettings();
if (!action.equals(Intent.ACTION_SEND)) {
updateUnread();
}
}
@Override
protected void onPause() {
super.onPause();
if (feedUpdater != null) {
feedUpdater.cancel(true);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (database != null) {
database.close();
}
}
private void updateUnread() {
runOnUiThread(new Runnable() {
public void run() {
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(getApplicationContext());
getDatabase();
int news = database.query(ARTICLE_TABLE, null, ARCHIVE + "=0", null, null, null, null).getCount();
btnGetPost.setText(String.format(getString(R.string.btnGetPost), news));
}
});
}
public void showToast(final String toast) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(Poche.this, toast, Toast.LENGTH_SHORT).show();
}
});
}
private void showErrorMessage(final String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
AlertDialog.Builder messageBox = new AlertDialog.Builder(Poche.this);
messageBox.setMessage(message);
messageBox.setTitle(getString(R.string.error));
// messageBox.setIconAttribute(android.R.attr.alertDialogIcon);
messageBox.setPositiveButton("OK", null);
messageBox.setCancelable(false);
messageBox.create().show();
}
});
}
@Override
public void feedUpdatedFinishedSuccessfully() {
showToast(getString(R.string.txtSyncDone));
updateUnread();
findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
@Override
public void feedUpdaterFinishedWithError(String errorMessage) {
showErrorMessage(getString(R.string.error_feed) + errorMessage);
updateUnread();
findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
}
@@ -1,142 +0,0 @@
package fr.gaulupeau.apps.Poche;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ScrollView;
import java.net.URL;
import fr.gaulupeau.apps.InThePoche.R;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARCHIVE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_AUTHOR;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_CONTENT;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_ID;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TABLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_TITLE;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.ARTICLE_URL;
import static fr.gaulupeau.apps.Poche.ArticlesSQLiteOpenHelper.MY_ID;
public class ReadArticle extends BaseActionBarActivity {
WebView webViewContent;
SQLiteDatabase database;
String id = "";
ScrollView view;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.article);
view = (ScrollView) findViewById(R.id.scroll);
ArticlesSQLiteOpenHelper helper = new ArticlesSQLiteOpenHelper(getApplicationContext());
database = helper.getWritableDatabase();
String[] getStrColumns = new String[]{ARTICLE_URL, MY_ID, ARTICLE_TITLE, ARTICLE_CONTENT, ARCHIVE, ARTICLE_AUTHOR};
Bundle data = getIntent().getExtras();
if (data != null) {
id = String.valueOf(data.getLong("id"));
}
Cursor ac = database.query(ARTICLE_TABLE, getStrColumns, MY_ID + "=" + id, null, null, null, null);
ac.moveToFirst();
String titleText = ac.getString(2);
String originalUrlText = ac.getString(0);
String originalUrlDesc = originalUrlText;
String htmlContent = ac.getString(3);
setTitle(titleText);
try {
URL originalUrl = new URL(originalUrlText);
originalUrlDesc = originalUrl.getHost();
} catch (Exception e) {
//
}
String htmlHeader = "<html>\n" +
"\t<head>\n" +
"\t\t<meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />\n" +
"\t\t<meta charset=\"utf-8\">\n" +
"\t\t<link rel=\"stylesheet\" href=\"main.css\" media=\"all\" id=\"main-theme\">\n" +
"\t\t<link rel=\"stylesheet\" href=\"ratatouille.css\" media=\"all\" id=\"extra-theme\">\n" +
"\t</head>\n" +
"\t\t<div id=\"main\">\n" +
"\t\t\t<body>\n" +
"\t\t\t\t<div id=\"content\" class=\"w600p center\">\n" +
"\t\t\t\t\t<div id=\"article\">\n" +
"\t\t\t\t\t\t<header class=\"mbm\">\n" +
"\t\t\t\t\t\t\t<h1>" + titleText + "</h1>\n" +
"\t\t\t\t\t\t\t<p>Open Original: <a href=\"" + originalUrlText + "\">" + originalUrlDesc + "</a></p>\n" +
"\t\t\t\t\t\t</header>\n" +
"\t\t\t\t\t\t<article>";
String htmlFooter = "</article>\n" +
"\t\t\t\t\t</div>\n" +
"\t\t\t\t</div>\n" +
"\t\t\t</body>\n" +
"\t\t</div>\n" +
"</html>";
webViewContent = (WebView) findViewById(R.id.webViewContent);
webViewContent.loadDataWithBaseURL("file:///android_asset/", htmlHeader + htmlContent + htmlFooter, "text/html", "utf-8", null);
Button btnMarkRead = (Button) findViewById(R.id.btnMarkRead);
btnMarkRead.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
markAsReadAndClose();
}
});
}
private void markAsReadAndClose() {
ContentValues values = new ContentValues();
values.put(ARCHIVE, 1);
database.update(ARTICLE_TABLE, values, MY_ID + "=" + id, null);
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_article, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuArticleMarkAsRead:
markAsReadAndClose();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
values.put("read_at", view.getScrollY());
database.update(ARTICLE_TABLE, values, ARTICLE_ID + "=" + id, null);
System.out.println(view.getScrollY());
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
database.close();
}
}
@@ -1,57 +0,0 @@
package fr.gaulupeau.apps.Poche;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
import fr.gaulupeau.apps.InThePoche.R;
import static fr.gaulupeau.apps.Poche.Helpers.PREFS_NAME;
public class Settings extends BaseActionBarActivity {
Button btnDone;
EditText editPocheUrl;
EditText editAPIUsername;
EditText editAPIToken;
TextView textViewVersion;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
String pocheUrl = settings.getString("pocheUrl", "http://");
String apiUsername = settings.getString("APIUsername", "");
String apiToken = settings.getString("APIToken", "");
editPocheUrl = (EditText) findViewById(R.id.pocheUrl);
editPocheUrl.setText(pocheUrl);
editAPIUsername = (EditText) findViewById(R.id.APIUsername);
editAPIUsername.setText(apiUsername);
editAPIToken = (EditText) findViewById(R.id.APIToken);
editAPIToken.setText(apiToken);
btnDone = (Button) findViewById(R.id.btnDone);
btnDone.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("pocheUrl", editPocheUrl.getText().toString());
editor.putString("APIUsername", editAPIUsername.getText().toString());
editor.putString("APIToken", editAPIToken.getText().toString());
editor.commit();
finish();
}
});
try {
textViewVersion = (TextView) findViewById(R.id.version);
textViewVersion.setText(BuildConfig.VERSION_NAME);
} catch (Exception e) {
//
}
}
}
@@ -0,0 +1,48 @@
package fr.gaulupeau.apps.Poche.data;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import de.greenrobot.dao.query.QueryBuilder;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
import fr.gaulupeau.apps.Poche.entity.DaoMaster;
import fr.gaulupeau.apps.Poche.entity.DaoSession;
/**
* @author Victor Häggqvist
* @since 10/19/15
*/
public class DbConnection {
private static final String TAG = DbConnection.class.getSimpleName();
private static Context context;
public static DaoSession getSession() {
if (Holder.session == null) {
// enable some debugging
if (BuildConfig.DEBUG) {
QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;
}
Log.d(TAG, "creating new db session");
DaoMaster.DevOpenHelper dbHelper = new DaoMaster.DevOpenHelper(context, "wallabag", null);
SQLiteDatabase db = dbHelper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
Holder.session = daoMaster.newSession();
} else {
Log.d(TAG, "using existing db session");
}
return Holder.session;
}
public static void setContext(Context context) {
DbConnection.context = context;
}
private static class Holder {
private static DaoSession session = getSession();
}
}
@@ -0,0 +1,287 @@
package fr.gaulupeau.apps.Poche.data;
import android.os.AsyncTask;
import android.util.Log;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import de.greenrobot.dao.DaoException;
import fr.gaulupeau.apps.Poche.entity.Article;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
import fr.gaulupeau.apps.Poche.entity.DaoSession;
import fr.gaulupeau.apps.Poche.util.arrays;
public class FeedUpdater extends AsyncTask<Void, Void, Void> {
private String wallabagUrl;
private String apiUserId;
private String apiToken;
private FeedUpdaterInterface callback;
private String errorMessage;
public FeedUpdater(String wallabagUrl, String apiUserId, String apiToken, FeedUpdaterInterface callback) {
this.wallabagUrl = wallabagUrl;
this.apiUserId = apiUserId;
this.apiToken = apiToken;
this.callback = callback;
}
@Override
protected Void doInBackground(Void... params) {
parseRSS();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (callback == null)
return;
if (errorMessage == null) {
callback.feedUpdatedFinishedSuccessfully();
} else {
callback.feedUpdaterFinishedWithError(errorMessage);
}
}
public void parseRSS() {
URL url;
// Set the url (you will need to change this to your RSS URL
try {
url = new URL(wallabagUrl + "/?feed&type=home&user_id=" + apiUserId + "&token=" + apiToken);
} catch (MalformedURLException e) {
e.printStackTrace();
}
OkHttpClient client = WallabagConnection.getClient();
String requestUrl = wallabagUrl + "/?feed&type=home&user_id=" + apiUserId + "&token=" + apiToken;
Request request = new Request.Builder()
.url(requestUrl)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
Document document = null;
try {
document = createDocument(response);
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
parseFeed(document);
}
private void parseFeed(Document document) {
DaoSession session = DbConnection.getSession();
ArticleDao articleDao = session.getArticleDao();
// This is the root node of each section you want to parse
NodeList itemLst = document.getElementsByTagName("item");
// This sets up some arrays to hold the data parsed
arrays.PodcastTitle = new String[itemLst.getLength()];
arrays.PodcastURL = new String[itemLst.getLength()];
arrays.PodcastContent = new String[itemLst.getLength()];
arrays.PodcastMedia = new String[itemLst.getLength()];
arrays.PodcastDate = new String[itemLst.getLength()];
arrays.PodcastId = new String[itemLst.getLength()];
// Loop through the XML passing the data to the arrays
for (int i = 0; i < itemLst.getLength(); i++) {
Node item = itemLst.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE) {
Element ielem = (Element) item;
// This section gets the elements from the XML
// that we want to use you will need to add
// and remove elements that you want / don't want
NodeList title = ielem.getElementsByTagName("title");
NodeList link = ielem.getElementsByTagName("link");
NodeList date = ielem.getElementsByTagName("pubDate");
NodeList content = ielem
.getElementsByTagName("description");
NodeList source = ielem.getElementsByTagName("source");
//NodeList media = ielem
// .getElementsByTagName("media:content");
// This is an attribute of an element so I create
// a string to make it easier to use
//String mediaurl = media.item(0).getAttributes()
// .getNamedItem("url").getNodeValue();
// This section adds an entry to the arrays with the
// data retrieved from above. I have surrounded each
// with try/catch just incase the element does not
// exist
try {
arrays.PodcastTitle[i] = cleanString(title.item(0).getChildNodes().item(0).getNodeValue());
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastTitle[i] = "Echec";
}
try {
arrays.PodcastDate[i] = date.item(0).getChildNodes().item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastDate[i] = null;
}
try {
arrays.PodcastURL[i] = link.item(0).getChildNodes()
.item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastURL[i] = "Echec";
}
try {
arrays.PodcastContent[i] = content.item(0)
.getChildNodes().item(0).getNodeValue();
} catch (NullPointerException e) {
e.printStackTrace();
arrays.PodcastContent[i] = "Echec";
}
NamedNodeMap sourceAttrs = source.item(0).getAttributes();
Node item1 = sourceAttrs.item(0);
String sourceUrl = item1.getNodeValue();
Map<String, String> urlQueryParams = getUrlQueryParams(sourceUrl);
String id = urlQueryParams.get("id");
arrays.PodcastId[i] = id;
fr.gaulupeau.apps.Poche.entity.Article article = new Article(null);
article.setTitle(arrays.PodcastTitle[i]);
article.setContent(arrays.PodcastContent[i]);
article.setUrl(arrays.PodcastURL[i]);
article.setArticleId(Integer.parseInt(arrays.PodcastId[i]));
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date date1 = null;
try {
date1 = dateFormat.parse(arrays.PodcastDate[i]);
article.setUpdateDate(date1);
} catch (ParseException e) {
e.printStackTrace();
}
article.setArchive(false);
article.setSync(false);
try {
Article existing = articleDao.queryBuilder()
.where(ArticleDao.Properties.ArticleId.eq(article.getArticleId()))
.build().uniqueOrThrow();
if (existing == null) {
articleDao.insert(article);
}
} catch (DaoException e) {
articleDao.insert(article);
}
// Log.d("foo", "insert " + article.getArticleId());
}
}
Log.d("foo", "articles "+ articleDao.count());
}
private Document createDocument(Response response) throws IOException, ParserConfigurationException, SAXException {
InputStream inputStream = response.body().byteStream();
// Retreive the XML from the URL
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
return documentBuilder.parse(inputStream);
}
public String cleanString(String s) {
s = s.replace("&Atilde;&copy;", "&eacute;");
s = s.replace("&Atilde;&uml;", "&egrave;");
s = s.replace("&Atilde;&ordf;", "&ecirc;");
s = s.replace("&Atilde;&laquo;", "&euml;");
s = s.replace("&Atilde;&nbsp;", "&agrave;");
s = s.replace("&Atilde;&curren;", "&auml;");
s = s.replace("&Atilde;&cent;", "&acirc;");
s = s.replace("&Atilde;&sup1;", "&ugrave;");
s = s.replace("&Atilde;&raquo;", "&ucirc;");
s = s.replace("&Atilde;&frac14;", "&uuml;");
s = s.replace("&Atilde;&acute;", "&ocirc;");
s = s.replace("&Atilde;&para;", "&ouml;");
s = s.replace("&Atilde;&reg;", "&icirc;");
s = s.replace("&Atilde;&macr;", "&iuml;");
s = s.replace("&Atilde;&sect;", "&ccedil;");
s = s.replace("&amp;", "&amp;");
// Replace multiple whitespaces with single space
s = s.replaceAll("\\s+", " ");
s = s.trim();
return s;
}
/**
* Split up URL params a la http://stackoverflow.com/a/13592567/1592572
*/
private Map<String, String> getUrlQueryParams(String surl) {
URL url = null;
try {
url = (new URI(surl)).toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
try {
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return query_pairs;
}
}
@@ -0,0 +1,10 @@
package fr.gaulupeau.apps.Poche.data;
/**
* Created by kevinmeyer on 13/12/14.
*/
public interface FeedUpdaterInterface {
void feedUpdaterFinishedWithError(String errorMessage);
void feedUpdatedFinishedSuccessfully();
}
@@ -0,0 +1,71 @@
package fr.gaulupeau.apps.Poche.data;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.entity.Article;
/**
* @author Victor Häggqvist
* @since 10/19/15
*/
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
private List<Article> articles;
private OnItemClickListener listener;
public ListAdapter(List<Article> articles, OnItemClickListener listener) {
this.articles = articles;
this.listener = listener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new ViewHolder(view, listener);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(articles.get(position));
}
@Override
public int getItemCount() {
return articles.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
OnItemClickListener listener;
TextView title;
TextView url;
public ViewHolder(View itemView, OnItemClickListener listener) {
super(itemView);
this.listener = listener;
title = (TextView) itemView.findViewById(R.id.title);
url = (TextView) itemView.findViewById(R.id.url);
itemView.setOnClickListener(this);
}
public void bind(Article article) {
title.setText(article.getTitle());
url.setText(article.getUrl());
}
@Override
public void onClick(View v) {
listener.onItemClick(getAdapterPosition());
}
}
public interface OnItemClickListener {
void onItemClick(int position);
}
}
@@ -0,0 +1,46 @@
package fr.gaulupeau.apps.Poche.data;
import android.content.Context;
import android.content.SharedPreferences;
/**
* @author Victor Häggqvist
* @since 10/20/15
*/
public class Settings {
private static final String PREFS_NAME = "InThePoche"; // keeping prefname for backwards compat
public static final String URL = "pocheUrl";
public static final String USER_ID = "APIUsername";
public static final String TOKEN = "APIToken";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
public static final String VERSION_CODE = "version_code";
private SharedPreferences pref;
public Settings(Context context) {
pref = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
public void setString(String key, String value) {
pref.edit().putString(key, value).commit();
}
public String getUrl() {
return pref.getString(URL, null);
}
public String getKey(String key) {
return pref.getString(key, null);
}
public void setAppVersion(int versionCode) {
pref.edit().putInt(VERSION_CODE, versionCode).commit();
}
public boolean hasUpdateChecher() {
return pref.getInt("update_checker", -1) != -1;
}
}
@@ -0,0 +1,114 @@
package fr.gaulupeau.apps.Poche.data;
import android.util.Log;
import com.facebook.stetho.okhttp.StethoInterceptor;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
/**
* @author Victor Häggqvist
* @since 10/20/15
*/
public class WallabagConnection {
public static OkHttpClient getClient() {
if (Holder.client != null)
return Holder.client;
OkHttpClient client = new OkHttpClient();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
client.setCookieHandler(cookieManager);
}
try {
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
client.setSslSocketFactory(sslSocketFactory);
client.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {}
if (BuildConfig.DEBUG) {
client.interceptors().add(new LoggingInterceptor());
client.networkInterceptors().add(new StethoInterceptor());
}
Holder.client = client;
return client;
}
private static class Holder {
private static OkHttpClient client = getClient();
}
/**
* OkHttp Logging interceptor
* http://stackoverflow.com/a/30625572/1592572
*/
static class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Log.d("OkHttp", String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Log.d("OkHttp", String.format("Received response for %s in %.1fms, status %d%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.code(), response.headers()));
return response;
}
}
}
@@ -0,0 +1,85 @@
package fr.gaulupeau.apps.Poche.data;
import android.util.Log;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.net.URL;
/**
* @author Victor Häggqvist
* @since 10/20/15
*/
public class WallabagService {
private String endpoint;
private final String username;
private final String password;
private OkHttpClient client;
public WallabagService(String endpoint, String username, String password) {
this.endpoint = endpoint;
this.username = username;
this.password = password;
client = WallabagConnection.getClient();
}
public void addLink(String link) throws IOException {
doLogin();
HttpUrl url = HttpUrl.parse(endpoint)
.newBuilder()
.setQueryParameter("plainurl", link)
.build();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).execute();
}
private boolean doLogin() throws IOException {
String url = endpoint+"/?login";
RequestBody formBody = new FormEncodingBuilder()
.add("login", username)
.add("password", password)
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Response response = client.newCall(request).execute();
return response.code() == 200;
}
public boolean toogleArchive(int articleId) throws IOException {
doLogin();
HttpUrl url = HttpUrl.parse(endpoint)
.newBuilder()
.setQueryParameter("action", "toggle_archive")
.setQueryParameter("id", Integer.toString(articleId))
.build();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
Log.d("foo", String.valueOf(response.code()));
return response.code() == 200;
}
}
@@ -0,0 +1,20 @@
package fr.gaulupeau.apps.Poche.ui;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
import fr.gaulupeau.apps.InThePoche.R;
public class AddActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
EditText pageUrl = (EditText) findViewById(R.id.page_url);
}
}
@@ -0,0 +1,115 @@
package fr.gaulupeau.apps.Poche.ui;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Patterns;
import android.widget.Toast;
import java.io.IOException;
import java.util.regex.Matcher;
import fr.gaulupeau.apps.Poche.App;
import fr.gaulupeau.apps.Poche.data.Settings;
import fr.gaulupeau.apps.Poche.data.WallabagService;
public class BagItProxyActivity extends AppCompatActivity {
private static final String TAG = BagItProxyActivity.class.getSimpleName();
private ProgressDialog mProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
final String extraText = extras.getString("android.intent.extra.TEXT");
final String pageUrl;
// Parsing string for urls.
Matcher matcher = Patterns.WEB_URL.matcher(extraText);
if (matcher.find()) {
pageUrl = matcher.group();
} else {
new AlertDialog.Builder(this)
.setTitle("Fail")
.setMessage("Couldn't find a URL in share string:\n" + extraText)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// nop
}
}).create();
return;
}
Settings settings = ((App) getApplication()).getSettings();
Log.d(TAG, "Baging " + pageUrl);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("Baging page");
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.show();
new AddTask(pageUrl, settings).execute();
}
private class AddTask extends AsyncTask<Void, Void, Boolean> {
private final String url;
private final String endpoint;
private final String username;
private final String password;
private String errorMessage;
public AddTask(String url, Settings settings) {
this.url = url;
endpoint = settings.getUrl();
username = settings.getKey(Settings.USERNAME);
password = settings.getKey(Settings.PASSWORD);
}
@Override
protected Boolean doInBackground(Void... params) {
WallabagService service = new WallabagService(endpoint, username, password);
try {
service.addLink(url);
return true;
} catch (IOException e) {
errorMessage = e.getMessage();
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
Toast.makeText(BagItProxyActivity.this, "Added", Toast.LENGTH_SHORT).show();
} else {
new AlertDialog.Builder(BagItProxyActivity.this)
.setTitle("Fail")
.setMessage(errorMessage)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
}
Log.d(TAG, "Baging done");
mProgressDialog.dismiss();
BagItProxyActivity.this.finish();
}
}
}
@@ -1,4 +1,4 @@
package fr.gaulupeau.apps.Poche;
package fr.gaulupeau.apps.Poche.ui;
import android.annotation.TargetApi;
@@ -0,0 +1,24 @@
package fr.gaulupeau.apps.Poche.ui;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
/**
* @author Victor Häggqvist
* @since 10/20/15
*/
public class ConnectionFailAlert {
public static AlertDialog getDialog(Context context, String message) {
return new AlertDialog.Builder(context)
.setTitle("Connection Failure")
.setMessage(message)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// nop
}
}).create();
}
}
@@ -0,0 +1,105 @@
package fr.gaulupeau.apps.Poche.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.List;
import de.greenrobot.dao.query.LazyList;
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.data.DbConnection;
import fr.gaulupeau.apps.Poche.data.ListAdapter;
import fr.gaulupeau.apps.Poche.entity.Article;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
import fr.gaulupeau.apps.Poche.entity.DaoSession;
public class ListArticlesActivity extends BaseActionBarActivity implements ListAdapter.OnItemClickListener {
private RecyclerView readList;
private boolean showAll = false;
private DaoSession mSession;
private List<Article> mArticles;
private ArticleDao mArticleDao;
private ListAdapter mAdapter;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
readList = (RecyclerView) findViewById(R.id.article_list);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
readList.setLayoutManager(layoutManager);
mSession = DbConnection.getSession();
mArticleDao = mSession.getArticleDao();
mArticles = new ArrayList<>();
mAdapter = new ListAdapter(mArticles, this);
readList.setAdapter(mAdapter);
}
@Override
protected void onResume() {
super.onResume();
updateList();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_list, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.menuShowAll).setTitle(getString(showAll ? R.string.menuShowUnread : R.string.menuShowAll));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuShowAll:
showAll = !showAll;
updateList();
return true;
case R.id.menuWipeDb:
mSession.getArticleDao().deleteAll();
updateList();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void updateList() {
LazyList<Article> articles = mArticleDao.queryBuilder()
.where(ArticleDao.Properties.Archive.notEq(true))
.orderDesc(ArticleDao.Properties.UpdateDate)
.limit(50)
.listLazy();
mArticles.clear();
mArticles.addAll(articles);
mAdapter.notifyDataSetChanged();
}
@Override
public void onItemClick(int position) {
Article article = mArticles.get(position);
Intent intent = new Intent(this, ReadArticleActivity.class);
intent.putExtra(ReadArticleActivity.EXTRA_ID, article.getId());
startActivity(intent);
}
}
@@ -0,0 +1,163 @@
/**
* Android to Poche
* A simple app to make the full save bookmark to Poche
* web page available via the Share menu on Android tablets
* @author GAULUPEAU Jonathan
* August 2013
*/
package fr.gaulupeau.apps.Poche.ui;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import de.greenrobot.dao.query.LazyList;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.App;
import fr.gaulupeau.apps.Poche.data.FeedUpdater;
import fr.gaulupeau.apps.Poche.data.FeedUpdaterInterface;
import fr.gaulupeau.apps.Poche.data.DbConnection;
import fr.gaulupeau.apps.Poche.data.Settings;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
import fr.gaulupeau.apps.Poche.entity.DaoSession;
/**
* Main activity class
*/
@TargetApi(Build.VERSION_CODES.FROYO)
public class PocheActivity extends Activity implements FeedUpdaterInterface {
Button btnGetPost;
Button btnSync;
Button btnSettings;
String action;
private FeedUpdater feedUpdater;
private Settings settings;
private String mUrl;
private String mUserId;
private String mToken;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
action = intent.getAction();
settings = ((App) getApplication()).getSettings();
getSettings();
setContentView(R.layout.main);
checkAndHandleAfterUpdate();
btnSync = (Button) findViewById(R.id.btnSync);
btnSync.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateFeed();
}
});
btnGetPost = (Button) findViewById(R.id.btnArticles);
btnGetPost.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getBaseContext(), ListArticlesActivity.class));
}
});
btnSettings = (Button) findViewById(R.id.btnSettings);
btnSettings.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(getBaseContext(), SettingsActivity.class));
}
});
}
private void updateFeed() {
if (mUrl == null) {
Toast.makeText(PocheActivity.this, R.string.txtConfigNotSet, Toast.LENGTH_SHORT).show();
return;
}
// Run update task
findViewById(R.id.progressBar1).setVisibility(View.VISIBLE);
feedUpdater = new FeedUpdater(mUrl, mUserId, mToken, this);
feedUpdater.execute();
}
private void checkAndHandleAfterUpdate() {
if (settings.hasUpdateChecher()) {
new AlertDialog.Builder(this)
.setTitle("App update")
.setMessage("Some app update message")
.setPositiveButton("OK", null)
.setCancelable(false)
.create().show();
}
settings.setAppVersion(BuildConfig.VERSION_CODE);
}
private void getSettings() {
mUrl = settings.getKey(Settings.URL);
mUserId = settings.getKey(Settings.USER_ID);
mToken = settings.getKey(Settings.TOKEN);
}
@Override
protected void onResume() {
super.onResume();
getSettings();
updateUnread();
}
@Override
protected void onPause() {
super.onPause();
if (feedUpdater != null) {
feedUpdater.cancel(true);
}
}
private void updateUnread() {
DaoSession session = DbConnection.getSession();
ArticleDao articleDao = session.getArticleDao();
LazyList<fr.gaulupeau.apps.Poche.entity.Article> articles = articleDao.queryBuilder().where(ArticleDao.Properties.Archive.eq(false)).build().listLazy();
btnGetPost.setText(String.format(getString(R.string.btnGetPost), articles.size()));
}
@Override
public void feedUpdatedFinishedSuccessfully() {
Toast.makeText(PocheActivity.this, R.string.txtSyncDone, Toast.LENGTH_SHORT).show();
updateUnread();
findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
@Override
public void feedUpdaterFinishedWithError(String errorMessage) {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.error_feed) + errorMessage)
.setTitle(getString(R.string.error))
.setPositiveButton("OK", null)
.setCancelable(false)
.create().show();
updateUnread();
findViewById(R.id.progressBar1).setVisibility(View.GONE);
}
}
@@ -0,0 +1,165 @@
package fr.gaulupeau.apps.Poche.ui;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.Toast;
import java.io.IOException;
import java.net.URL;
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.App;
import fr.gaulupeau.apps.Poche.data.DbConnection;
import fr.gaulupeau.apps.Poche.data.Settings;
import fr.gaulupeau.apps.Poche.data.WallabagService;
import fr.gaulupeau.apps.Poche.entity.Article;
import fr.gaulupeau.apps.Poche.entity.ArticleDao;
import fr.gaulupeau.apps.Poche.entity.DaoSession;
public class ReadArticleActivity extends BaseActionBarActivity {
public static final String EXTRA_ID = "ReadArticleActivity.id";
WebView webViewContent;
private Article mArticle;
private ArticleDao mArticleDao;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.article);
Intent intent = getIntent();
long articleId = intent.getLongExtra(EXTRA_ID, -1);
DaoSession session = DbConnection.getSession();
mArticleDao = session.getArticleDao();
mArticle = mArticleDao.queryBuilder().where(ArticleDao.Properties.Id.eq(articleId)).build().unique();
String titleText = mArticle.getTitle();
String originalUrlText = mArticle.getUrl();
String originalUrlDesc = originalUrlText;
String htmlContent = mArticle.getContent();
setTitle(titleText);
try {
URL originalUrl = new URL(originalUrlText);
originalUrlDesc = originalUrl.getHost();
} catch (Exception e) {
//
}
String htmlHeader = "<html>\n" +
"\t<head>\n" +
"\t\t<meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />\n" +
"\t\t<meta charset=\"utf-8\">\n" +
"\t\t<link rel=\"stylesheet\" href=\"main.css\" media=\"all\" id=\"main-theme\">\n" +
"\t\t<link rel=\"stylesheet\" href=\"ratatouille.css\" media=\"all\" id=\"extra-theme\">\n" +
"\t</head>\n" +
"\t\t<div id=\"main\">\n" +
"\t\t\t<body>\n" +
"\t\t\t\t<div id=\"content\" class=\"w600p center\">\n" +
"\t\t\t\t\t<div id=\"article\">\n" +
"\t\t\t\t\t\t<header class=\"mbm\">\n" +
"\t\t\t\t\t\t\t<h1>" + titleText + "</h1>\n" +
"\t\t\t\t\t\t\t<p>Open Original: <a href=\"" + originalUrlText + "\">" + originalUrlDesc + "</a></p>\n" +
"\t\t\t\t\t\t</header>\n" +
"\t\t\t\t\t\t<article>";
String htmlFooter = "</article>\n" +
"\t\t\t\t\t</div>\n" +
"\t\t\t\t</div>\n" +
"\t\t\t</body>\n" +
"\t\t</div>\n" +
"</html>";
webViewContent = (WebView) findViewById(R.id.webViewContent);
webViewContent.loadDataWithBaseURL("file:///android_asset/", htmlHeader + htmlContent + htmlFooter, "text/html", "utf-8", null);
Button btnMarkRead = (Button) findViewById(R.id.btnMarkRead);
btnMarkRead.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
markAsReadAndClose();
}
});
}
private void markAsReadAndClose() {
new ToggleArchiveTask(mArticle.getArticleId()).execute();
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_article, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuArticleMarkAsRead:
markAsReadAndClose();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private class ToggleArchiveTask extends AsyncTask<Void, Void, Boolean> {
private int articleId;
private WallabagService service;
private String errorMessage;
public ToggleArchiveTask(int articleId) {
this.articleId = articleId;
}
@Override
protected void onPreExecute() {
mArticle.setArchive(!mArticle.getArchive());
mArticleDao.update(mArticle);
Settings settings = ((App) getApplication()).getSettings();
service = new WallabagService(
settings.getUrl(),
settings.getKey(Settings.USERNAME),
settings.getKey(Settings.PASSWORD));
}
@Override
protected Boolean doInBackground(Void... params) {
try {
return service.toogleArchive(articleId);
} catch (IOException e) {
errorMessage = e.getMessage();
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
mArticle.setSync(true);
mArticleDao.update(mArticle);
} else {
ConnectionFailAlert.getDialog(ReadArticleActivity.this, errorMessage).show();
}
Toast.makeText(ReadArticleActivity.this, "Archived", Toast.LENGTH_SHORT).show();
}
}
}
@@ -0,0 +1,65 @@
package fr.gaulupeau.apps.Poche.ui;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.App;
import fr.gaulupeau.apps.Poche.data.Settings;
public class SettingsActivity extends BaseActionBarActivity {
Button btnDone;
EditText editPocheUrl;
EditText editAPIUsername;
EditText editAPIToken;
TextView textViewVersion;
EditText username;
EditText password;
private Settings settings;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
settings = ((App) getApplication()).getSettings();
String pocheUrl = settings.getKey(Settings.URL);
String apiUsername = settings.getKey(Settings.USER_ID);
String apiToken = settings.getKey(Settings.TOKEN);
editPocheUrl = (EditText) findViewById(R.id.pocheUrl);
editPocheUrl.setText(pocheUrl == null ? "http://" : pocheUrl);
editAPIUsername = (EditText) findViewById(R.id.APIUsername);
editAPIUsername.setText(apiUsername);
editAPIToken = (EditText) findViewById(R.id.APIToken);
editAPIToken.setText(apiToken);
username = (EditText) findViewById(R.id.username);
username.setText(settings.getKey(Settings.USERNAME));
password = (EditText) findViewById(R.id.password);
password.setText(settings.getKey(Settings.PASSWORD));
btnDone = (Button) findViewById(R.id.btnDone);
btnDone.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
settings.setString(Settings.URL, editPocheUrl.getText().toString());
settings.setString(Settings.USER_ID, editAPIUsername.getText().toString());
settings.setString(Settings.TOKEN, editAPIToken.getText().toString());
settings.setString(Settings.USERNAME, username.getText().toString());
settings.setString(Settings.PASSWORD, password.getText().toString());
finish();
}
});
textViewVersion = (TextView) findViewById(R.id.version);
textViewVersion.setText(BuildConfig.VERSION_NAME);
}
}
@@ -1,4 +1,4 @@
package fr.gaulupeau.apps.Poche;
package fr.gaulupeau.apps.Poche.util;
public class arrays {
public static String[] PodcastTitle;
@@ -6,4 +6,5 @@ public class arrays {
public static String[] PodcastContent;
public static String[] PodcastMedia;
public static String[] PodcastDate;
public static String[] PodcastId;
}
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="fr.gaulupeau.apps.Poche.ui.AddActivity"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Page URL"
android:id="@+id/page_url" />
</android.support.design.widget.TextInputLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bag It"
android:id="@+id/button" />
</LinearLayout>
+1 -1
View File
@@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/scroll"
tools:context="fr.gaulupeau.apps.Poche.ReadArticle" >
tools:context="fr.gaulupeau.apps.Poche.ui.ReadArticleActivity" >
<LinearLayout
android:layout_width="match_parent"
+5 -6
View File
@@ -6,13 +6,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="fr.gaulupeau.apps.Poche.ListArticles" >
tools:context="fr.gaulupeau.apps.Poche.ui.ListArticlesActivity" >
<ListView
android:id="@+id/liste_articles"
<android.support.v7.widget.RecyclerView
android:id="@+id/article_list"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
android:layout_height="match_parent"/>
</LinearLayout>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text" />
</LinearLayout>
+2 -2
View File
@@ -5,7 +5,7 @@
android:id="@+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:context="fr.gaulupeau.apps.Poche.Poche" >
tools:context="fr.gaulupeau.apps.Poche.ui.PocheActivity" >
<LinearLayout
android:layout_width="fill_parent"
@@ -43,7 +43,7 @@
android:background="#000000" />
<Button
android:id="@+id/btnGetPost"
android:id="@+id/btnArticles"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
+48 -2
View File
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="fr.gaulupeau.apps.Poche.Settings">
tools:context="fr.gaulupeau.apps.Poche.ui.SettingsActivity">
<LinearLayout
android:layout_width="match_parent"
@@ -49,7 +49,7 @@
android:layout_weight="1"
android:ems="10"
android:hint="@string/txtAPIUsername"
android:inputType="text" />
android:inputType="textVisiblePassword" />
</android.support.design.widget.TextInputLayout>
@@ -77,6 +77,52 @@
android:paddingTop="5sp"
android:text="@string/instructions" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Extra"
android:id="@+id/textView" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/toppadding"
android:layout_weight="1"
android:autoLink="web"
android:paddingTop="5sp"
android:text="To be able to post back to wallabag" />
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:hint="Username"
android:inputType="text" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:hint="Password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/btnDone"
android:layout_width="fill_parent"
+1 -1
View File
@@ -4,6 +4,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/menuArticleMarkAsRead"
android:title="@string/btnMarkRead"
android:icon="@android:drawable/checkbox_on_background"
android:icon="@drawable/ic_done_24dp"
app:showAsAction="always" />
</menu>
@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
+3
View File
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="toppadding">10px</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
+8
View File
@@ -2,4 +2,12 @@
<resources>
<style name="app_theme" parent="Theme.AppCompat.Light.DarkActionBar"/>
<style name="mainActivity_theme" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="ProxyTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>
</resources>
+3 -3
View File
@@ -2,15 +2,15 @@
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.android.tools.build:gradle:1.4.0-beta3'
}
}
allprojects {
repositories {
mavenCentral()
jcenter()
}
}
+1
View File
@@ -0,0 +1 @@
/build
+6
View File
@@ -0,0 +1,6 @@
apply plugin: 'java'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'de.greenrobot:greendao-generator:2.0.0'
}
@@ -0,0 +1,27 @@
package com.snilius;
import java.io.IOException;
import de.greenrobot.daogenerator.DaoGenerator;
import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Schema;
public class GenerateDao {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "fr.gaulupeau.apps.Poche.entity");
Entity article = schema.addEntity("Article");
article.addIdProperty();
article.addIntProperty("articleId").columnName("article_id").unique();
article.addStringProperty("content").columnName("content");
article.addStringProperty("author").columnName("author");
article.addStringProperty("title").columnName("title");
article.addStringProperty("url").columnName("url");
article.addBooleanProperty("archive").columnName("archive");
article.addBooleanProperty("sync").columnName("sync");
article.addDateProperty("updateDate").columnName("update_date");
new DaoGenerator().generateAll(schema, "/home/victor/AndroidStudioProjects/wallabag/app/src-gen");
}
}
+1 -1
View File
@@ -1 +1 @@
include ':app'
include ':app', ':gen-dao'