mirror of
https://github.com/wallabag/android-app.git
synced 2026-05-19 15:10:37 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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("é", "é");
|
||||
s = s.replace("è", "è");
|
||||
s = s.replace("ê", "ê");
|
||||
s = s.replace("ë", "ë");
|
||||
s = s.replace("Ã ", "à");
|
||||
s = s.replace("ä", "ä");
|
||||
s = s.replace("â", "â");
|
||||
s = s.replace("ù", "ù");
|
||||
s = s.replace("û", "û");
|
||||
s = s.replace("ü", "ü");
|
||||
s = s.replace("ô", "ô");
|
||||
s = s.replace("ö", "ö");
|
||||
s = s.replace("î", "î");
|
||||
s = s.replace("ï", "ï");
|
||||
s = s.replace("ç", "ç");
|
||||
s = s.replace("&", "&");
|
||||
|
||||
// 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("é", "é");
|
||||
s = s.replace("è", "è");
|
||||
s = s.replace("ê", "ê");
|
||||
s = s.replace("ë", "ë");
|
||||
s = s.replace("Ã ", "à");
|
||||
s = s.replace("ä", "ä");
|
||||
s = s.replace("â", "â");
|
||||
s = s.replace("ù", "ù");
|
||||
s = s.replace("û", "û");
|
||||
s = s.replace("ü", "ü");
|
||||
s = s.replace("ô", "ô");
|
||||
s = s.replace("ö", "ö");
|
||||
s = s.replace("î", "î");
|
||||
s = s.replace("ï", "ï");
|
||||
s = s.replace("ç", "ç");
|
||||
s = s.replace("&", "&");
|
||||
|
||||
// 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
-1
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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
@@ -1 +1 @@
|
||||
include ':app'
|
||||
include ':app', ':gen-dao'
|
||||
|
||||
Reference in New Issue
Block a user