Compare commits

..

4 Commits

21 changed files with 142 additions and 57 deletions

View File

@ -46,12 +46,30 @@ You can directly import this project into IntellIJ/Android Studio.
You'll have to:
- Configure Fabric, or [remove it](https://docs.fabric.io/android/fabric/settings/removing.html#).
- Configure fabric and add your `apiKey` and `apiSecret` in the `fabric.properties` file.
- Create a firebase project and add the `google-services.json` to the `app/` folder.
- Define the following in `res/values/strings.xml` or create `res/values/secrets.xml`
- Define the following some parameters either in `~/.gradle/gradle.properties` or as gradle parameters (see the examples)
- mercury: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
- feedback_email: An email to receive users feedback.
- source_url: an url to the source code, used in the settings
- tracker_url: an url to the tracker, used in the settings
- To run your app, you'll have to add `-P appLoginUrl="your.selfoss-instance.url" -P appLoginUsername="Username" -P appLoginPassword="password"`. (These are only used to run the espresso tests. You should be able to use empty strings or fake values to build the app)
- mercuryApiKey: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
- feedbackEmail: An email to receive users feedback.
- sourceUrl: an url to the source code, used in the settings
- trackerUrl: an url to the tracker, used in the settings
### Examples:
#### Inside ~/.gradle/gradle.properties
```
appLoginUrl="URL"
appLoginUsername="LOGIN"
appLoginPassword="PASS"
mercuryApiKey="LONGAPIKEY"
feedbackEmail="EMAIL"
sourceUrl="URLSOURCE"
trackerUrl="URLTRACKER"
```
#### As gradle parameters
```
./gradlew .... -P appLoginUrl="URL" -P appLoginUsername="LOGIN" -P appLoginPassword="PASS" -P mercuryApiKey="LONGAPIKEY" -P feedbackEmail="EMAIL" -P sourceUrl="URLSOURCE" -P trackerUrl="URLTRACKER"
```

2
.gitignore vendored
View File

@ -214,6 +214,4 @@ gradle-app.setting
# End of https://www.gitignore.io/api/java,gradle,android,androidstudio
secrets.xml
release/

View File

@ -1,3 +1,11 @@
**1.5.2.18/19**
- APK minification finally working. That means less space taken !
**1.5.2.17**
- Source code and tracker links weren't being set, and updated the contributing doc.
**1.5.2.15/16**
- Adding an account header on the lateral drawer.

View File

@ -53,10 +53,16 @@ android {
// tests
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField "String", "MERCURY_KEY", mercuryApiKey
buildConfigField "String", "FEEDBACK_EMAIL", feedbackEmail
buildConfigField "String", "SOURCE_URL", sourceUrl
buildConfigField "String", "TRACKER_URL", trackerUrl
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
@ -143,7 +149,7 @@ dependencies {
compile('com.github.bumptech.glide:okhttp3-integration:4.1.0@aar')
// Asking politely users to rate the app
compile 'com.github.stkent:amplify:1.5.0'
compile 'com.github.stkent:amplify:2.1.0'
// For the article reader
compile 'com.klinkerapps:drag-dismiss-activity:1.4.3'
@ -165,6 +171,7 @@ apply plugin: 'com.google.gms.google-services'
afterEvaluate {
initFabricPropertiesIfNeeded()
initAppLoginPropertiesIfNeeded()
initAppForSecretPropertiesIfNeeded()
}
def initFabricPropertiesIfNeeded() {
@ -189,3 +196,16 @@ def initAppLoginPropertiesIfNeeded() {
}
}
}
def initAppForSecretPropertiesIfNeeded() {
def propertiesFile = file(System.getProperty("user.home") + '/.gradle/gradle.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated local property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: System.getProperty("user.home") + "/.gradle/gradle.properties", comment: commentMessage) {
entry(key: "mercuryApiKey", value: System.getProperty("mercuryApiKey"))
entry(key: "feedbackEmail", value: System.getProperty("feedbackEmail"))
entry(key: "sourceUrl", value: System.getProperty("sourceUrl"))
entry(key: "trackerUrl", value: System.getProperty("trackerUrl"))
}
}
}

View File

@ -60,4 +60,8 @@
# self signed glidemodule
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * implements com.bumptech.glide.module.GlideModule
-dontwarn com.anupcowkur.reservoir.**
-dontwarn javax.annotation.**

View File

@ -45,7 +45,7 @@ class AddSourceActivity : AppCompatActivity() {
try {
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
api = SelfossApi(this, this@AddSourceActivity, prefs.getBoolean("isSelfSignedCert", false))
api = SelfossApi(this, this@AddSourceActivity, prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("should_log_everything", false))
} catch (e: IllegalArgumentException) {
mustLoginToAddSource()
}

View File

@ -147,7 +147,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
customTabActivityHelper = CustomTabActivityHelper()
val dirtyPref = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false))
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false), dirtyPref.getBoolean("should_log_everything", false))
items = ArrayList()
appColors = AppColors(this@HomeActivity)

View File

@ -211,7 +211,7 @@ class LoginActivity : AppCompatActivity() {
editor.putBoolean("isSelfSignedCert", isWithSelfSignedCert)
editor.apply()
val api = SelfossApi(this, this@LoginActivity, isWithSelfSignedCert)
val api = SelfossApi(this, this@LoginActivity, isWithSelfSignedCert, isWithSelfSignedCert)
api.login().enqueue(object : Callback<SuccessResponse> {
private fun preferenceError(t: Throwable) {
editor.remove("url")

View File

@ -12,6 +12,8 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.crashlytics.android.Crashlytics
import com.ftinc.scoop.Scoop
import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector
import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector
import com.github.stkent.amplify.tracking.Amplify
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
@ -49,7 +51,8 @@ class MyApp : MultiDexApplication() {
private fun initAmplify() {
Amplify.initSharedInstance(this)
.setFeedbackEmailAddress(getString(R.string.feedback_email))
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL))
.applyAllDefaultRules()
}

View File

@ -51,7 +51,7 @@ class ReaderActivity : DragDismissActivity() {
val title: TextView = v.findViewById(R.id.title)
val content: HtmlTextView = v.findViewById(R.id.content)
val url = intent.getStringExtra("url")
val parser = MercuryApi(getString(R.string.mercury))
val parser = MercuryApi(BuildConfig.MERCURY_KEY)
val browserBtn: ImageButton = v.findViewById(R.id.browserBtn)
val shareBtn: ImageButton = v.findViewById(R.id.shareBtn)

View File

@ -40,7 +40,7 @@ class SourcesActivity : AppCompatActivity() {
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val api = SelfossApi(this, this@SourcesActivity, prefs.getBoolean("isSelfSignedCert", false))
val api = SelfossApi(this, this@SourcesActivity, prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("should_log_everything", false))
var items: ArrayList<Sources> = ArrayList()
mFab.attachToRecyclerView(mRecyclerView)

View File

@ -2,20 +2,20 @@ package apps.amine.bou.readerforselfoss.api.mercury
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
class ParsedContent(val title: String,
val content: String,
val date_published: String,
val lead_image_url: String,
val dek: String,
val url: String,
val domain: String,
val excerpt: String,
val total_pages: Int,
val rendered_pages: Int,
val next_page_url: String) : Parcelable {
class ParsedContent(@SerializedName("title") val title: String,
@SerializedName("content") val content: String,
@SerializedName("date_published") val date_published: String,
@SerializedName("lead_image_url") val lead_image_url: String,
@SerializedName("dek") val dek: String,
@SerializedName("url") val url: String,
@SerializedName("domain") val domain: String,
@SerializedName("excerpt") val excerpt: String,
@SerializedName("total_pages") val total_pages: Int,
@SerializedName("rendered_pages") val rendered_pages: Int,
@SerializedName("next_page_url") val next_page_url: String) : Parcelable {
companion object {
@JvmField val CREATOR: Parcelable.Creator<ParsedContent> = object : Parcelable.Creator<ParsedContent> {

View File

@ -19,10 +19,13 @@ import retrofit2.converter.gson.GsonConverterFactory
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
import okhttp3.logging.HttpLoggingInterceptor
// codebeat:disable[ARITY,TOO_MANY_FUNCTIONS]
class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Boolean) {
class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Boolean, shouldLog: Boolean) {
private lateinit var service: SelfossService
private val config: Config = Config(c)
@ -42,14 +45,13 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
.with("basic", BasicAuthenticator(this))
.build()
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient {
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder {
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
return OkHttpClient
.Builder()
.maybeWithSelfSigned(isWithSelfSignedCert)
.authenticator(CachingAuthenticatorDecorator(this, authCache))
.addInterceptor(AuthenticationCacheInterceptor(authCache))
.build()
}
@ -69,13 +71,23 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
.setLenient()
.create()
val logging = HttpLoggingInterceptor()
logging.level = if (shouldLog)
HttpLoggingInterceptor.Level.BODY
else
HttpLoggingInterceptor.Level.NONE
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert)
httpClient.addInterceptor(logging)
try {
val retrofit =
Retrofit
.Builder()
.baseUrl(config.baseUrl)
.client(authenticator.getHttpClien(isWithSelfSignedCert))
.client(httpClient.build())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
service = retrofit.create(SelfossService::class.java)

View File

@ -7,7 +7,7 @@ import android.os.Parcelable
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
import com.google.gson.annotations.SerializedName
private fun constructUrl(config: Config?, path: String, file: String): String {
@ -19,23 +19,28 @@ private fun constructUrl(config: Config?, path: String, file: String): String {
}
data class Tag(val tag: String, val color: String, val unread: Int)
data class Tag(@SerializedName("tag") val tag: String,
@SerializedName("color") val color: String,
@SerializedName("unread") val unread: Int)
class SuccessResponse(val success: Boolean) {
class SuccessResponse(@SerializedName("success") val success: Boolean) {
val isSuccess: Boolean
get() = success
}
class Stats(val total: Int, val unread: Int, val starred: Int)
class Stats(@SerializedName("total") val total: Int,
@SerializedName("unread") val unread: Int,
@SerializedName("starred") val starred: Int)
data class Spout(val name: String, val description: String)
data class Spout(@SerializedName("name") val name: String,
@SerializedName("description") val description: String)
data class Sources(val id: String,
val title: String,
val tags: String,
val spout: String,
val error: String,
val icon: String) {
data class Sources(@SerializedName("id") val id: String,
@SerializedName("title") val title: String,
@SerializedName("tags") val tags: String,
@SerializedName("spout") val spout: String,
@SerializedName("error") val error: String,
@SerializedName("icon") val icon: String) {
var config: Config? = null
fun getIcon(app: Context): String {
@ -47,15 +52,15 @@ data class Sources(val id: String,
}
data class Item(val id: String,
val datetime: String,
val title: String,
val unread: Boolean,
val starred: Boolean,
val thumbnail: String,
val icon: String,
val link: String,
val sourcetitle: String) : Parcelable {
data class Item(@SerializedName("id") val id: String,
@SerializedName("datetime") val datetime: String,
@SerializedName("title") val title: String,
@SerializedName("unread") val unread: Boolean,
@SerializedName("starred") val starred: Boolean,
@SerializedName("thumbnail") val thumbnail: String,
@SerializedName("icon") val icon: String,
@SerializedName("link") val link: String,
@SerializedName("sourcetitle") val sourcetitle: String) : Parcelable {
var config: Config? = null

View File

@ -27,6 +27,7 @@ import android.widget.Toast;
import java.util.List;
import apps.amine.bou.readerforselfoss.BuildConfig;
import apps.amine.bou.readerforselfoss.R;
import apps.amine.bou.readerforselfoss.utils.Config;
import com.ftinc.scoop.ui.ScoopSettingsActivity;
@ -242,7 +243,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
findPreference( "trackerLink" ).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
openUrl(Uri.parse(getString(R.string.tracker_url)));
openUrl(Uri.parse(BuildConfig.TRACKER_URL));
return true;
}
});
@ -250,7 +251,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
findPreference("sourceLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
openUrl(Uri.parse(getString(R.string.source_url)));
openUrl(Uri.parse(BuildConfig.SOURCE_URL));
return false;
}
});

View File

@ -147,4 +147,7 @@
<string name="unique_id_to_clipboard">Texte copié</string>
<string name="display_header_drawer_summary">Afficher une entête avec l\'url de votre instance de Selfoss en haut du drawer lateral.</string>
<string name="display_header_drawer_title">Entête de compte</string>
<string name="login_everything_title">Log de tous les appels à l\'API</string>
<string name="login_everything_off">Aucun appel à l\'API ne sera logué</string>
<string name="login_everything_on">Tous les appels à l\'API vont êtres logués</string>
</resources>

View File

@ -148,4 +148,7 @@
<string
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
<string name="display_header_drawer_title">Account header</string>
<string name="login_everything_title">Logging every api calls</string>
<string name="login_everything_off">No api call will be logged</string>
<string name="login_everything_on">This will log every api call for debug purpose.</string>
</resources>

View File

@ -150,4 +150,7 @@
<string
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
<string name="display_header_drawer_title">Account header</string>
<string name="login_everything_title">Logging every api calls</string>
<string name="login_everything_on">This will log every api call for debug purpose.</string>
<string name="login_everything_off">No api call will be logged</string>
</resources>

View File

@ -1,6 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:key="should_log_everything"
android:summaryOff="@string/login_everything_off"
android:summaryOn="@string/login_everything_on"
android:title="@string/login_everything_title" />
<SwitchPreference
android:defaultValue="false"
android:key="loging_debug"

View File

@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-beta4'
classpath 'com.android.tools.build:gradle:3.0.0-beta5'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

Binary file not shown.