Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
8157146498 | |||
94d23888b1 | |||
737fe9bb4a | |||
0051ed2e73 | |||
e0595957e2 | |||
8d09ff7fdb | |||
04feb66b07 | |||
54b2ac7f24 | |||
12356a35fa | |||
12262304ac | |||
c58f97452e | |||
eb3872f7a6 | |||
9fa178d513 | |||
043b184065 | |||
10559bb894 | |||
d0000d66b2 | |||
b447ac738a | |||
faebfc238c | |||
c28fbd37cc | |||
4b8396959d | |||
b39d510e07 | |||
286dda7f80 |
36
.github/CONTRIBUTING.md
vendored
36
.github/CONTRIBUTING.md
vendored
@ -22,7 +22,8 @@ Always check if the web version of your instance is working.
|
||||
### Bug reports/Feature request
|
||||
|
||||
* Always search before reporting an issue or asking for a feature to avoid duplicates.
|
||||
* Include every usefull details (app version, phone model, Android version and screenshots when possible)
|
||||
* Include your unique user id. It's displayed on the debug settings page. (You can tap it, it'll be copied to your clipboard)
|
||||
* Include every other useful details (app version, phone model, Android version and screenshots when possible).
|
||||
* Avoid bumping non-fatal issues, or feature requests. I'll try to fix them as soon as possible, and try to prioritize the requests. (You may wan to use the [reactions](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) for that)
|
||||
|
||||
### Pull requests
|
||||
@ -45,12 +46,31 @@ 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. **It can be empty.**
|
||||
- trackerUrl: an url to the tracker, used in the settings. **It can be empty.**
|
||||
- appLoginUrl, appLoginUsername and appLoginPassword: url, username and password of a selfoss instance. **These are only used for tests. They can be empty if you don't test API calls.**
|
||||
|
||||
### Examples:
|
||||
#### Inside ~/.gradle/gradle.properties
|
||||
|
||||
```
|
||||
appLoginUrl="URL" # It can be empty.
|
||||
appLoginUsername="LOGIN" # It can be empty.
|
||||
appLoginPassword="PASS" # It can be empty.
|
||||
mercuryApiKey="LONGAPIKEY"
|
||||
feedbackEmail="EMAIL"
|
||||
sourceUrl="URLSOURCE" # It can be empty.
|
||||
trackerUrl="URLTRACKER" # It can be empty.
|
||||
```
|
||||
|
||||
#### 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"
|
||||
```
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -214,6 +214,6 @@ gradle-app.setting
|
||||
|
||||
# End of https://www.gitignore.io/api/java,gradle,android,androidstudio
|
||||
|
||||
secrets.xml
|
||||
release/
|
||||
|
||||
release/
|
||||
crowdin.properties
|
43
CHANGELOG.md
43
CHANGELOG.md
@ -1,3 +1,46 @@
|
||||
**1.5.3.01/02**
|
||||
|
||||
- Added translation link to the settings page.
|
||||
|
||||
- Added the translation link to the README.
|
||||
|
||||
**1.5.3.00**
|
||||
|
||||
- (BETA) Added pull from bottom to load more pages of results. May be buggy.
|
||||
|
||||
**1.5.2.18/19**
|
||||
|
||||
- APK minification finally working. That means less space taken !
|
||||
- Added an option to log every API call.
|
||||
|
||||
**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.
|
||||
|
||||
- The account header is only displayed when the setting is enabled.
|
||||
|
||||
**1.5.2.13/14**
|
||||
|
||||
- Updated glide.
|
||||
|
||||
- Loading images from self signed certificate now working.
|
||||
|
||||
**1.5.2.12**
|
||||
|
||||
- Self signed certificates are now working for loading data. Image are not loading yet.
|
||||
|
||||
**1.5.2.11**
|
||||
|
||||
- Added a random unique identifier to be used in the logs.
|
||||
|
||||
**1.5.2.08/09/10**
|
||||
|
||||
- Added settable logs for reading articles problems.
|
||||
|
||||
**1.5.2.07**
|
||||
|
||||
- Added the ability to choose the number of items loaded (the maximum value is 200 and is imposed by the selfoss api)
|
||||
|
15
README.md
15
README.md
@ -1,17 +1,17 @@
|
||||
# ReaderForSelfoss
|
||||
|
||||
[](https://crowdin.com/project/readerforselfoss) [](https://gitter.im/amine-bou/ReaderForSelfoss)
|
||||
|
||||
[](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
|
||||
|
||||
[](https://gitter.im/amine-bou/ReaderForSelfoss)
|
||||
|
||||
[](https://codebeat.co/projects/github-com-aminecmi-readerforselfoss-master)
|
||||
|
||||
[](https://www.codetriage.com/aminecmi/readerforselfoss)
|
||||
[](https://codebeat.co/projects/github-com-aminecmi-readerforselfoss-master) [](https://www.codetriage.com/aminecmi/readerforselfoss)
|
||||
|
||||
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
|
||||
|
||||
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
|
||||
|
||||
The last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
|
||||
|
||||
|
||||
## Want to help ?
|
||||
|
||||
@ -22,8 +22,3 @@ Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob
|
||||
- [Check what changed](https://github.com/aminecmi/ReaderforSelfoss/blob/master/CHANGELOG.md)
|
||||
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
|
||||
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
|
||||
|
||||
|
||||
#Icon
|
||||
|
||||
I used RSS Document by AlfredoCreates.com from the Noun Project. You can see it [here](https://thenounproject.com/search/?q=rss&i=596333).
|
@ -23,6 +23,8 @@ def versionNameFromGit() {
|
||||
return gitVersion().trim()
|
||||
}
|
||||
|
||||
apply plugin: 'org.sonarqube'
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'io.fabric'
|
||||
@ -30,12 +32,14 @@ apply plugin: 'io.fabric'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
maven {
|
||||
url 'https://maven.fabric.io/public'
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.0"
|
||||
buildToolsVersion '26.0.2'
|
||||
defaultConfig {
|
||||
applicationId "apps.amine.bou.readerforselfoss"
|
||||
minSdkVersion 16
|
||||
@ -53,10 +57,17 @@ 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
|
||||
buildConfigField "String", "TRANSLATION_URL", translationUrl
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||
'proguard-rules.pro'
|
||||
}
|
||||
@ -83,12 +94,12 @@ android {
|
||||
|
||||
dependencies {
|
||||
// Testing
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.0'
|
||||
androidTestCompile 'com.android.support.test:runner:1.0.0'
|
||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:3.0.0'
|
||||
// Espresso-intents for validation and stubbing of Intents
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-intents:3.0.0'
|
||||
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
@ -119,13 +130,13 @@ dependencies {
|
||||
compile 'agency.tango.android:material-intro-screen:0.0.5'
|
||||
|
||||
// About
|
||||
compile('com.mikepenz:aboutlibraries:5.9.6@aar') {
|
||||
compile('com.mikepenz:aboutlibraries:5.9.7@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
// Retrofit + http logging + okhttp
|
||||
compile 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
|
||||
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||
compile 'com.burgstaller:okhttp-digest:1.12'
|
||||
|
||||
@ -137,10 +148,11 @@ dependencies {
|
||||
compile 'org.sufficientlysecure:html-textview:3.3'
|
||||
|
||||
// glide
|
||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||
compile 'com.github.bumptech.glide:glide:4.1.1'
|
||||
compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||
|
||||
// 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'
|
||||
@ -162,6 +174,7 @@ apply plugin: 'com.google.gms.google-services'
|
||||
afterEvaluate {
|
||||
initFabricPropertiesIfNeeded()
|
||||
initAppLoginPropertiesIfNeeded()
|
||||
initAppForSecretPropertiesIfNeeded()
|
||||
}
|
||||
|
||||
def initFabricPropertiesIfNeeded() {
|
||||
@ -186,3 +199,17 @@ 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"))
|
||||
entry(key: "translationUrl", value: System.getProperty("translationUrl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
app/proguard-rules.pro
vendored
15
app/proguard-rules.pro
vendored
@ -56,4 +56,17 @@
|
||||
|
||||
|
||||
#Bottom bar lib
|
||||
-dontwarn com.roughike.bottombar.**
|
||||
-dontwarn com.roughike.bottombar.**
|
||||
|
||||
|
||||
# self signed glidemodule
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
-dontwarn com.anupcowkur.reservoir.**
|
||||
|
||||
-dontwarn javax.annotation.**
|
@ -28,7 +28,8 @@
|
||||
android:name=".IntroActivity"
|
||||
android:theme="@style/Theme.Intro">
|
||||
</activity>
|
||||
<activity android:name=".LoginActivity"
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:label="@string/title_activity_login">
|
||||
</activity>
|
||||
<activity android:name=".HomeActivity">
|
||||
@ -41,13 +42,15 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="apps.amine.bou.readerforselfoss.HomeActivity" />
|
||||
</activity>
|
||||
<activity android:name=".SourcesActivity"
|
||||
<activity
|
||||
android:name=".SourcesActivity"
|
||||
android:parentActivityName=".HomeActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".HomeActivity" />
|
||||
</activity>
|
||||
<activity android:name=".AddSourceActivity"
|
||||
<activity
|
||||
android:name=".AddSourceActivity"
|
||||
android:parentActivityName=".SourcesActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
@ -61,9 +64,14 @@
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".ReaderActivity"
|
||||
<activity
|
||||
android:name=".ReaderActivity"
|
||||
android:theme="@style/DragDismissTheme">
|
||||
</activity>
|
||||
|
||||
<meta-data
|
||||
android:name="apps.amine.bou.readerforselfoss.utils.glide.SelfSignedGlideModule"
|
||||
android:value="GlideModule" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -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()
|
||||
}
|
||||
|
@ -64,6 +64,9 @@ import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.mikepenz.materialdrawer.AccountHeader
|
||||
import com.mikepenz.materialdrawer.AccountHeaderBuilder
|
||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
|
||||
|
||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
@ -80,6 +83,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
private var items: ArrayList<Item> = ArrayList()
|
||||
private var clickBehavior = false
|
||||
private var debugReadingItems = false
|
||||
private var internalBrowser = false
|
||||
private var articleViewer = false
|
||||
private var shouldBeCardView = false
|
||||
@ -91,6 +95,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var maybeTagFilter: Tag? = null
|
||||
private var maybeSourceFilter: Sources? = null
|
||||
private var maybeSearchFilter: String? = null
|
||||
private var userIdentifier: String = ""
|
||||
private var displayAccountHeader: Boolean = false
|
||||
private var infiniteScroll: Boolean = false
|
||||
|
||||
private lateinit var emptyText: TextView
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
@ -108,6 +115,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private lateinit var sharedPref: SharedPreferences
|
||||
private lateinit var firebaseRemoteConfig: FirebaseRemoteConfig
|
||||
private lateinit var appColors: AppColors
|
||||
private var offset: Int = 0
|
||||
private var firstVisible: Int = 0
|
||||
private var recyclerViewScrollListener: RecyclerView.OnScrollListener? = null
|
||||
|
||||
|
||||
|
||||
|
||||
@ -140,15 +151,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
|
||||
val dirtyPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false))
|
||||
val dirtyPref = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false), dirtyPref.getBoolean("should_log_everything", false))
|
||||
items = ArrayList()
|
||||
|
||||
appColors = AppColors(this@HomeActivity)
|
||||
|
||||
handleBottomBar()
|
||||
|
||||
handleDrawer()
|
||||
handleDrawer(dirtyPref)
|
||||
|
||||
coordinatorLayout = findViewById(R.id.coordLayout)
|
||||
swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout)
|
||||
@ -277,6 +288,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
|
||||
private fun handleSharedPrefs() {
|
||||
debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
||||
clickBehavior = sharedPref.getBoolean("tab_on_tap", false)
|
||||
internalBrowser = sharedPref.getBoolean("prefer_internal_browser", true)
|
||||
articleViewer = sharedPref.getBoolean("prefer_article_viewer", true)
|
||||
@ -285,52 +297,58 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
displayAllCount = sharedPref.getBoolean("display_other_count", false)
|
||||
fullHeightCards = sharedPref.getBoolean("full_height_cards", false)
|
||||
itemsNumber = sharedPref.getString("prefer_api_items_number", "200").toInt()
|
||||
userIdentifier = sharedPref.getString("unique_id", "")
|
||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||
}
|
||||
|
||||
private fun handleDrawer() {
|
||||
|
||||
drawer = DrawerBuilder()
|
||||
.withActivity(this)
|
||||
.withRootView(R.id.drawer_layout)
|
||||
.withToolbar(toolbar)
|
||||
.withActionBarDrawerToggle(true)
|
||||
.withActionBarDrawerToggleAnimated(true)
|
||||
.withShowDrawerOnFirstLaunch(true)
|
||||
.withOnDrawerListener(object: Drawer.OnDrawerListener {
|
||||
override fun onDrawerSlide(v: View?, p1: Float) {
|
||||
bottomBar.alpha = (1 - p1)
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(v: View?) {
|
||||
bottomBar.show()
|
||||
}
|
||||
|
||||
override fun onDrawerOpened(v: View?) {
|
||||
bottomBar.hide()
|
||||
}
|
||||
|
||||
})
|
||||
.build()
|
||||
|
||||
drawer.addStickyFooterItem(
|
||||
PrimaryDrawerItem()
|
||||
.withName(R.string.action_about)
|
||||
.withSelectable(false)
|
||||
.withIcon(R.drawable.ic_info_outline)
|
||||
.withIconTintingEnabled(true)
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
LibsBuilder()
|
||||
.withActivityStyle(
|
||||
if (appColors.isDarkTheme)
|
||||
Libs.ActivityStyle.LIGHT_DARK_TOOLBAR
|
||||
else
|
||||
Libs.ActivityStyle.DARK
|
||||
private fun handleDrawer(dirtyPref: SharedPreferences) {
|
||||
displayAccountHeader =
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("account_header_displaying", false)
|
||||
val headerResult: AccountHeader? = if (displayAccountHeader) {
|
||||
AccountHeaderBuilder()
|
||||
.withActivity(this)
|
||||
.withHeaderBackground(R.drawable.bg)
|
||||
.addProfiles(
|
||||
ProfileDrawerItem()
|
||||
.withName(
|
||||
dirtyPref.getString("url", "")
|
||||
)
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this@HomeActivity)
|
||||
false
|
||||
.withIcon(resources.getDrawable(R.mipmap.ic_launcher))
|
||||
)
|
||||
.withSelectionListEnabledForSingleProfile(false)
|
||||
.build()
|
||||
} else null
|
||||
|
||||
val drawerBuilder =
|
||||
DrawerBuilder()
|
||||
.withActivity(this)
|
||||
.withRootView(R.id.drawer_layout)
|
||||
.withToolbar(toolbar)
|
||||
.withActionBarDrawerToggle(true)
|
||||
.withActionBarDrawerToggleAnimated(true)
|
||||
.withShowDrawerOnFirstLaunch(true)
|
||||
.withOnDrawerListener(object: Drawer.OnDrawerListener {
|
||||
override fun onDrawerSlide(v: View?, p1: Float) {
|
||||
bottomBar.alpha = (1 - p1)
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(v: View?) {
|
||||
bottomBar.show()
|
||||
}
|
||||
|
||||
override fun onDrawerOpened(v: View?) {
|
||||
bottomBar.hide()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
if (displayAccountHeader && headerResult != null)
|
||||
drawerBuilder.withAccountHeader(headerResult)
|
||||
|
||||
drawer = drawerBuilder.build()
|
||||
|
||||
drawer.addStickyFooterItem(
|
||||
PrimaryDrawerItem()
|
||||
.withName(R.string.title_activity_settings)
|
||||
@ -362,7 +380,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
else {
|
||||
for (tag in maybeTags) {
|
||||
val gd: GradientDrawable = GradientDrawable()
|
||||
val gd = GradientDrawable()
|
||||
gd.setColor(Color.parseColor(tag.color))
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
@ -434,6 +452,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
.withIdentifier(DRAWER_ID_TAGS)
|
||||
.withSelectable(false))
|
||||
handleTags(maybeDrawerData.tags)
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_item_sources))
|
||||
@ -446,6 +465,26 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
)
|
||||
handleSources(maybeDrawerData.sources)
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
PrimaryDrawerItem()
|
||||
.withName(R.string.action_about)
|
||||
.withSelectable(false)
|
||||
.withIcon(R.drawable.ic_info_outline)
|
||||
.withIconTintingEnabled(true)
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
LibsBuilder()
|
||||
.withActivityStyle(
|
||||
if (appColors.isDarkTheme)
|
||||
Libs.ActivityStyle.LIGHT_DARK_TOOLBAR
|
||||
else
|
||||
Libs.ActivityStyle.DARK
|
||||
)
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this@HomeActivity)
|
||||
false
|
||||
})
|
||||
|
||||
|
||||
if (!loadedFromCache)
|
||||
@ -534,6 +573,30 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
recyclerView.layoutManager = mLayoutManager
|
||||
recyclerView.setHasFixedSize(true)
|
||||
|
||||
if (infiniteScroll) {
|
||||
if (recyclerViewScrollListener == null)
|
||||
recyclerViewScrollListener = object: RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) {
|
||||
if (dy > 0) {
|
||||
if (localRecycler != null) {
|
||||
val lastVisibleItem: Int = when (mLayoutManager) {
|
||||
is StaggeredGridLayoutManager -> mLayoutManager.findLastCompletelyVisibleItemPositions(null).last()
|
||||
is GridLayoutManager -> mLayoutManager.findLastCompletelyVisibleItemPosition()
|
||||
else -> 0
|
||||
}
|
||||
|
||||
if (lastVisibleItem == (items.size - 1)) {
|
||||
getElementsAccordingToTab(appendResults = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recyclerView.clearOnScrollListeners()
|
||||
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
}
|
||||
|
||||
bottomBar.setTabSelectedListener(object: BottomNavigationBar.OnTabSelectedListener {
|
||||
override fun onTabUnselected(position: Int) = Unit
|
||||
|
||||
@ -572,26 +635,31 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
recyclerView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun getElementsAccordingToTab() =
|
||||
private fun getElementsAccordingToTab(appendResults: Boolean = false) =
|
||||
when (elementsShown) {
|
||||
UNREAD_SHOWN -> getUnRead()
|
||||
READ_SHOWN -> getRead()
|
||||
FAV_SHOWN -> getStarred()
|
||||
else -> getUnRead()
|
||||
UNREAD_SHOWN -> getUnRead(appendResults)
|
||||
READ_SHOWN -> getRead(appendResults)
|
||||
FAV_SHOWN -> getStarred(appendResults)
|
||||
else -> getUnRead(appendResults)
|
||||
}
|
||||
|
||||
private fun doCallTo(toastMessage: Int, call: (String?, Long?, String?) -> Call<List<Item>>) {
|
||||
private fun doCallTo(appendResults: Boolean, toastMessage: Int, call: (String?, Long?, String?) -> Call<List<Item>>) {
|
||||
fun handleItemsResponse(response: Response<List<Item>>) {
|
||||
val didUpdate = (response.body() != items)
|
||||
if (response.body() != null) {
|
||||
if (response.body() != items) {
|
||||
items = response.body() as ArrayList<Item>
|
||||
if (appendResults)
|
||||
items.addAll(response.body() as ArrayList<Item>)
|
||||
else
|
||||
items = response.body() as ArrayList<Item>
|
||||
}
|
||||
} else {
|
||||
items = ArrayList()
|
||||
if (!appendResults)
|
||||
items = ArrayList()
|
||||
}
|
||||
if (didUpdate)
|
||||
handleListResult()
|
||||
handleListResult(appendResults)
|
||||
|
||||
mayBeEmpty()
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
@ -612,22 +680,40 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
})
|
||||
}
|
||||
|
||||
private fun getUnRead() {
|
||||
private fun getUnRead(appendResults: Boolean = false) {
|
||||
offset = if (appendResults) (offset + itemsNumber)
|
||||
else 0
|
||||
firstVisible = if (appendResults) firstVisible else 0
|
||||
elementsShown = UNREAD_SHOWN
|
||||
doCallTo(R.string.cant_get_new_elements){t, id, f -> api.newItems(t, id, f, itemsNumber)}
|
||||
doCallTo(appendResults, R.string.cant_get_new_elements){t, id, f -> api.newItems(t, id, f, itemsNumber, offset)}
|
||||
}
|
||||
|
||||
private fun getRead() {
|
||||
private fun getRead(appendResults: Boolean = false) {
|
||||
offset = if (appendResults) (offset + itemsNumber)
|
||||
else 0
|
||||
firstVisible = if (appendResults) firstVisible else 0
|
||||
elementsShown = READ_SHOWN
|
||||
doCallTo(R.string.cant_get_read){t, id, f -> api.readItems(t, id, f)}
|
||||
doCallTo(appendResults, R.string.cant_get_read){t, id, f -> api.readItems(t, id, f, itemsNumber, offset)}
|
||||
}
|
||||
|
||||
private fun getStarred() {
|
||||
private fun getStarred(appendResults: Boolean = false) {
|
||||
offset = if (appendResults) (offset + itemsNumber)
|
||||
else 0
|
||||
firstVisible = if (appendResults) firstVisible else 0
|
||||
elementsShown = FAV_SHOWN
|
||||
doCallTo(R.string.cant_get_favs){t, id, f -> api.starredItems(t, id, f)}
|
||||
doCallTo(appendResults, R.string.cant_get_favs){t, id, f -> api.starredItems(t, id, f, itemsNumber, offset)}
|
||||
}
|
||||
|
||||
private fun handleListResult() {
|
||||
private fun handleListResult(appendResults: Boolean = false) {
|
||||
if (appendResults) {
|
||||
val oldManager = recyclerView.layoutManager
|
||||
firstVisible = if ((oldManager is StaggeredGridLayoutManager)) {
|
||||
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
||||
}
|
||||
else
|
||||
(oldManager as GridLayoutManager)?.findFirstCompletelyVisibleItemPosition()
|
||||
}
|
||||
|
||||
reloadLayoutManager()
|
||||
|
||||
val mAdapter: RecyclerView.Adapter<*>
|
||||
@ -641,7 +727,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
fullHeightCards,
|
||||
appColors)
|
||||
appColors,
|
||||
debugReadingItems,
|
||||
userIdentifier)
|
||||
} else {
|
||||
mAdapter =
|
||||
ItemListAdapter(
|
||||
@ -651,11 +739,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
customTabActivityHelper,
|
||||
clickBehavior,
|
||||
internalBrowser,
|
||||
articleViewer)
|
||||
articleViewer,
|
||||
debugReadingItems,
|
||||
userIdentifier)
|
||||
}
|
||||
recyclerView.adapter = mAdapter
|
||||
mAdapter.notifyDataSetChanged()
|
||||
|
||||
if (appendResults) {
|
||||
recyclerView.scrollToPosition(firstVisible!!)
|
||||
}
|
||||
|
||||
reloadBadges()
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ class IntroActivity : MaterialIntroActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
|
||||
addSlide(SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimary)
|
||||
|
@ -16,14 +16,6 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.*
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
@ -31,7 +23,12 @@ import apps.amine.bou.readerforselfoss.utils.checkAndDisplayStoreApk
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.ftinc.scoop.Scoop
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
@ -51,6 +48,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
private lateinit var mPasswordView: EditText
|
||||
private lateinit var mHTTPPasswordView: EditText
|
||||
private lateinit var mLoginFormView: View
|
||||
private lateinit var userIdentifier: String
|
||||
private var logErrors: Boolean = false
|
||||
|
||||
|
||||
@ -76,6 +74,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
userIdentifier = settings.getString("unique_id", "")
|
||||
logErrors = settings.getBoolean("loging_debug", false)
|
||||
|
||||
editor = settings.edit()
|
||||
@ -209,7 +208,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")
|
||||
@ -224,6 +223,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
mHTTPLoginView.error = getString(R.string.wrong_infos)
|
||||
mHTTPPasswordView.error = getString(R.string.wrong_infos)
|
||||
if (logErrors) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "LOGIN_DEBUG_ERRROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
Toast.makeText(this@LoginActivity, t.message, Toast.LENGTH_LONG).show()
|
||||
|
@ -6,18 +6,26 @@ import android.net.Uri
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.multidex.MultiDexApplication
|
||||
import android.widget.ImageView
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.anupcowkur.reservoir.Reservoir
|
||||
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
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import java.io.IOException
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
|
||||
|
||||
|
||||
class MyApp : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Fabric.with(this, Crashlytics())
|
||||
@ -26,6 +34,13 @@ class MyApp : MultiDexApplication() {
|
||||
|
||||
initCache()
|
||||
|
||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
if (prefs.getString("unique_id", "").isEmpty()) {
|
||||
val editor = prefs.edit()
|
||||
editor.putString("unique_id", randomUUID().toString())
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
initDrawerImageLoader()
|
||||
|
||||
initTheme()
|
||||
@ -36,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()
|
||||
}
|
||||
|
||||
@ -51,11 +67,15 @@ class MyApp : MultiDexApplication() {
|
||||
private fun initDrawerImageLoader() {
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(imageView: ImageView?, uri: Uri?, placeholder: Drawable?, tag: String?) {
|
||||
Glide.with(imageView?.context).load(uri).placeholder(placeholder).into(imageView)
|
||||
Glide.with(imageView?.context)
|
||||
.load(uri)
|
||||
.apply(RequestOptions.fitCenterTransform()
|
||||
.placeholder(placeholder))
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
override fun cancel(imageView: ImageView?) {
|
||||
Glide.clear(imageView)
|
||||
Glide.with(imageView?.context).clear(imageView)
|
||||
}
|
||||
|
||||
override fun placeholder(ctx: Context?, tag: String?): Drawable {
|
||||
|
@ -1,7 +1,5 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -9,22 +7,21 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.bumptech.glide.Glide
|
||||
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
|
||||
import org.sufficientlysecure.htmltextview.HtmlTextView
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import xyz.klinker.android.drag_dismiss.activity.DragDismissActivity
|
||||
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.ftinc.scoop.Scoop
|
||||
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
|
||||
import org.sufficientlysecure.htmltextview.HtmlTextView
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import xyz.klinker.android.drag_dismiss.activity.DragDismissActivity
|
||||
|
||||
|
||||
class ReaderActivity : DragDismissActivity() {
|
||||
@ -50,7 +47,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)
|
||||
|
||||
@ -75,9 +72,9 @@ class ReaderActivity : DragDismissActivity() {
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isEmpty())
|
||||
Glide
|
||||
.with(baseContext)
|
||||
.load(response.body()!!.lead_image_url)
|
||||
.asBitmap()
|
||||
.fitCenter()
|
||||
.load(response.body()!!.lead_image_url)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(image)
|
||||
|
||||
shareBtn.setOnClickListener {
|
||||
|
@ -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)
|
||||
|
@ -2,15 +2,12 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.support.v7.widget.CardView
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
@ -22,15 +19,11 @@ import android.widget.Toast
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
@ -39,6 +32,11 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.*
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapFitCenter
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ItemCardAdapter(private val app: Activity,
|
||||
private val items: ArrayList<Item>,
|
||||
@ -47,7 +45,9 @@ class ItemCardAdapter(private val app: Activity,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean,
|
||||
private val fullHeightCards: Boolean,
|
||||
private val appColors: AppColors) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() {
|
||||
private val appColors: AppColors,
|
||||
val debugReadingItems: Boolean,
|
||||
val userIdentifier: String) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() {
|
||||
private val c: Context = app.baseContext
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
|
||||
@ -66,7 +66,7 @@ class ItemCardAdapter(private val app: Activity,
|
||||
holder.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
Glide.clear(holder.itemImage)
|
||||
Glide.with(c).clear(holder.itemImage)
|
||||
holder.itemImage.setImageDrawable(null)
|
||||
} else {
|
||||
if (fullHeightCards) {
|
||||
@ -130,11 +130,31 @@ class ItemCardAdapter(private val app: Activity,
|
||||
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||
if (debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
|
||||
Crashlytics.logException(Exception("Was success, but did it work ?"))
|
||||
|
||||
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
doUnmark(i, position)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
if (debugReadingItems) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show()
|
||||
items.add(i)
|
||||
notifyItemInserted(position)
|
||||
|
@ -3,16 +3,11 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.text.format.DateUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -21,24 +16,23 @@ import android.widget.*
|
||||
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.*
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class ItemListAdapter(private val app: Activity,
|
||||
@ -47,7 +41,9 @@ class ItemListAdapter(private val app: Activity,
|
||||
private val helper: CustomTabActivityHelper,
|
||||
private val clickBehavior: Boolean,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
|
||||
private val articleViewer: Boolean,
|
||||
val debugReadingItems: Boolean,
|
||||
val userIdentifier: String) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
private val c: Context = app.baseContext
|
||||
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
|
||||
@ -142,11 +138,31 @@ class ItemListAdapter(private val app: Activity,
|
||||
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {
|
||||
if (debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
|
||||
Crashlytics.logException(Exception("Was success, but did it work ?"))
|
||||
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
doUnmark(i, position)
|
||||
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
if (debugReadingItems) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show()
|
||||
items.add(i)
|
||||
notifyItemInserted(position)
|
||||
|
@ -21,7 +21,7 @@ import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.utils.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -2,11 +2,6 @@ package apps.amine.bou.readerforselfoss.api.selfoss
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.LoginActivity
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import com.burgstaller.okhttp.AuthenticationCacheInterceptor
|
||||
@ -23,13 +18,14 @@ import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.*
|
||||
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)
|
||||
@ -38,36 +34,7 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
|
||||
|
||||
fun OkHttpClient.Builder.maybeWithSelfSigned(isWithSelfSignedCert: Boolean): OkHttpClient.Builder =
|
||||
if (isWithSelfSignedCert) {
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> =
|
||||
arrayOf()
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
val sslContext = SSLContext.getInstance("SSL")
|
||||
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||
|
||||
val sslSocketFactory = sslContext.socketFactory
|
||||
|
||||
OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
getUnsafeHttpClient()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
@ -78,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()
|
||||
}
|
||||
|
||||
|
||||
@ -105,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)
|
||||
@ -123,17 +99,17 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
|
||||
fun login(): Call<SuccessResponse> =
|
||||
service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||
|
||||
fun readItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
|
||||
getItems("read", tag, sourceId, search, 200)
|
||||
fun readItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||
getItems("read", tag, sourceId, search, itemsNumber, offset)
|
||||
|
||||
fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int): Call<List<Item>> =
|
||||
getItems("unread", tag, sourceId, search, itemsNumber)
|
||||
fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||
getItems("unread", tag, sourceId, search, itemsNumber, offset)
|
||||
|
||||
fun starredItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
|
||||
getItems("starred", tag, sourceId, search, 200)
|
||||
fun starredItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||
|
||||
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int): Call<List<Item>> =
|
||||
service.getItems(type, tag, sourceId, search, userName, password, items)
|
||||
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int, offset: Int): Call<List<Item>> =
|
||||
service.getItems(type, tag, sourceId, search, userName, password, items, offset)
|
||||
|
||||
fun markItem(itemId: String): Call<SuccessResponse> =
|
||||
service.markAsRead(itemId, userName, password)
|
||||
|
@ -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
|
||||
|
||||
@ -109,14 +114,14 @@ data class Item(val id: String,
|
||||
// TODO: maybe find a better way to handle these kind of urls
|
||||
fun getLinkDecoded(): String {
|
||||
var stringUrl: String
|
||||
if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||
stringUrl = if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||
if (link.contains("&url=")) {
|
||||
stringUrl = link.substringAfter("&url=")
|
||||
link.substringAfter("&url=")
|
||||
} else {
|
||||
stringUrl = this.link.replace("&", "&")
|
||||
this.link.replace("&", "&")
|
||||
}
|
||||
} else {
|
||||
stringUrl = this.link.replace("&", "&")
|
||||
this.link.replace("&", "&")
|
||||
}
|
||||
|
||||
// handle :443 => https
|
||||
|
@ -23,7 +23,8 @@ internal interface SelfossService {
|
||||
@Query("search") search: String?,
|
||||
@Query("username") username: String,
|
||||
@Query("password") password: String,
|
||||
@Query("items") items: Int): Call<List<Item>>
|
||||
@Query("items") items: Int,
|
||||
@Query("offset") offset: Int): Call<List<Item>>
|
||||
|
||||
@POST("mark/{id}")
|
||||
fun markAsRead(@Path("id") id: String,
|
||||
|
@ -2,8 +2,11 @@ package apps.amine.bou.readerforselfoss.settings;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -11,6 +14,7 @@ import android.os.Bundle;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.support.v7.app.ActionBar;
|
||||
@ -19,10 +23,13 @@ import android.preference.PreferenceManager;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.view.MenuItem;
|
||||
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;
|
||||
|
||||
|
||||
@ -184,6 +191,25 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_debug);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
SharedPreferences pref = getActivity().getSharedPreferences(Config.Companion.getSettingsName(), Context.MODE_PRIVATE);
|
||||
final String id = pref.getString("unique_id", "...");
|
||||
|
||||
final Preference identifier = findPreference("debug_identifier");
|
||||
final ClipboardManager clipboard = (ClipboardManager)
|
||||
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
identifier.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
ClipData clip = ClipData.newPlainText("Selfoss unique id", id);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
|
||||
Toast.makeText(getActivity(), R.string.unique_id_to_clipboard, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
identifier.setTitle(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,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;
|
||||
}
|
||||
});
|
||||
@ -225,7 +251,15 @@ 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;
|
||||
}
|
||||
});
|
||||
|
||||
findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
openUrl(Uri.parse(BuildConfig.TRANSLATION_URL));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -5,8 +5,6 @@ import android.content.Context
|
||||
import android.support.annotation.ColorInt
|
||||
import android.util.TypedValue
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import java.lang.reflect.AccessibleObject.setAccessible
|
||||
|
||||
|
||||
|
||||
class AppColors(a: Activity) {
|
||||
|
@ -97,7 +97,7 @@ fun String.longHash(): Long {
|
||||
val l = this.length
|
||||
val chars = this.toCharArray()
|
||||
|
||||
for (i in 0..l - 1) {
|
||||
for (i in 0 until l) {
|
||||
h = 31 * h + chars[i].toLong()
|
||||
}
|
||||
return h
|
||||
|
@ -1,24 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||
|
||||
|
||||
fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
|
||||
Glide.with(this).load(url).asBitmap().centerCrop().into(iv)
|
||||
|
||||
fun Context.bitmapFitCenter(url: String, iv: ImageView) =
|
||||
Glide.with(this).load(url).asBitmap().fitCenter().into(iv)
|
||||
|
||||
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
|
||||
Glide.with(this).load(url).asBitmap().centerCrop().into(object : BitmapImageViewTarget(iv) {
|
||||
override fun setResource(resource: Bitmap) {
|
||||
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, resource)
|
||||
circularBitmapDrawable.isCircular = true
|
||||
iv.setImageDrawable(circularBitmapDrawable)
|
||||
}
|
||||
})
|
@ -0,0 +1,39 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
fun getUnsafeHttpClient() =
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> =
|
||||
arrayOf()
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
val sslContext = SSLContext.getInstance("SSL")
|
||||
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||
|
||||
val sslSocketFactory = sslContext.socketFactory
|
||||
|
||||
OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
@ -16,7 +16,7 @@ fun String.toTextDrawableString(): String {
|
||||
}
|
||||
|
||||
fun Item.sourceAndDateText(): String {
|
||||
var formattedDate: String = try {
|
||||
val formattedDate: String = try {
|
||||
" " + DateUtils.getRelativeTimeSpanString(
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
|
||||
Date().time,
|
||||
|
@ -7,14 +7,10 @@ import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
|
||||
import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder
|
||||
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.ReaderActivity
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
|
||||
import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder
|
||||
|
||||
|
||||
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
||||
|
@ -0,0 +1,29 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||
|
||||
|
||||
fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
|
||||
Glide.with(this).asBitmap().load(url).apply(RequestOptions.centerCropTransform()).into(iv)
|
||||
|
||||
fun Context.bitmapFitCenter(url: String, iv: ImageView) =
|
||||
Glide.with(this).asBitmap().load(url).apply(RequestOptions.fitCenterTransform()).into(iv)
|
||||
|
||||
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.apply(RequestOptions.centerCropTransform()).
|
||||
into(object : BitmapImageViewTarget(iv) {
|
||||
override fun setResource(resource: Bitmap?) {
|
||||
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, resource)
|
||||
circularBitmapDrawable.isCircular = true
|
||||
iv.setImageDrawable(circularBitmapDrawable)
|
||||
}
|
||||
})
|
@ -0,0 +1,32 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.glide
|
||||
|
||||
import android.content.Context
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.GlideBuilder
|
||||
import com.bumptech.glide.Registry
|
||||
import com.bumptech.glide.module.GlideModule
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class SelfSignedGlideModule : GlideModule {
|
||||
|
||||
override fun applyOptions(context: Context?, builder: GlideBuilder?) {}
|
||||
|
||||
override fun registerComponents(context: Context?, glide: Glide?, registry: Registry?) {
|
||||
|
||||
if (context != null) {
|
||||
val pref = context?.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
if (pref.getBoolean("isSelfSignedCert", false)) {
|
||||
val client = getUnsafeHttpClient().build()
|
||||
|
||||
registry?.append(GlideUrl::class.java, InputStream::class.java,
|
||||
com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
BIN
app/src/main/res/drawable/bg.png
Normal file
BIN
app/src/main/res/drawable/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 406 B |
@ -31,7 +31,7 @@
|
||||
<string name="cant_get_read">"Impossible de récupérer les éléments lus."</string>
|
||||
<string name="nothing_here">"Il n'y a rien ici !"</string>
|
||||
<string name="tab_new">"Non lus"</string>
|
||||
<string name="tab_read">"Lus"</string>
|
||||
<string name="tab_read">"Tous"</string>
|
||||
<string name="tab_favs">"Favoris"</string>
|
||||
<string name="action_about">"À propos"</string>
|
||||
<string name="marked_as_read">"Marqué comme lu"</string>
|
||||
@ -140,4 +140,16 @@
|
||||
<string name="self_signed_cert_warning">Pour des raisons de sécurités, les certificats auto-signés sont désactivés par défaut. En les activant, je ne serais pas responsable de quelconques problèmes de sécurité rencontrés.</string>
|
||||
<string name="pref_selfoss_category">Api Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
|
||||
<string name="read_debug_title">Des articles lus marqués comme non lus ?</string>
|
||||
<string name="read_debug_on">Les appels API vont être logués lorsequ\'un article est marqué comme lu</string>
|
||||
<string name="read_debug_off">Aucun log quand un article est marqué comme lu</string>
|
||||
<string name="summary_debug_identifier">Identifiant de debug</string>
|
||||
<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>
|
||||
<string name="pref_general_infinite_loading_title">(BETA) Charger plus d\'articles au scroll</string>
|
||||
<string name="translation">Traduction</string>
|
||||
</resources>
|
@ -140,4 +140,17 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="summary_debug_identifier">Debug identifier</string>
|
||||
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string>
|
||||
<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>
|
||||
<string name="pref_general_infinite_loading_title">(BETA) Load more articles on scroll</string>
|
||||
<string name="translation">Vertaling</string>
|
||||
</resources>
|
@ -31,7 +31,7 @@
|
||||
<string name="cant_get_read">"Can't get read articles"</string>
|
||||
<string name="nothing_here">"Nothing here"</string>
|
||||
<string name="tab_new">"New"</string>
|
||||
<string name="tab_read">"Read"</string>
|
||||
<string name="tab_read">"All"</string>
|
||||
<string name="tab_favs">"Favorites"</string>
|
||||
<string name="action_about">"About"</string>
|
||||
<string name="marked_as_read">"Item read"</string>
|
||||
@ -142,4 +142,17 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
<string name="summary_debug_identifier">Debug identifier</string>
|
||||
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string>
|
||||
<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>
|
||||
<string name="pref_general_infinite_loading_title">(BETA) Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
</resources>
|
@ -13,6 +13,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarDark" parent="MaterialDrawerTheme">
|
||||
@ -25,12 +26,14 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<!-- ToolBar -->
|
||||
<style name="ToolBarStyle" parent="Theme.AppCompat">
|
||||
<item name="android:textColorPrimary">@color/white</item>
|
||||
<item name="android:textColorSecondary">@color/white</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
<item name="actionMenuTextColor">@color/white</item>
|
||||
<!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item>
|
||||
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>-->
|
||||
@ -46,6 +49,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarBlueAmberDark" parent="MaterialDrawerTheme">
|
||||
@ -58,6 +62,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarGreyOrange" parent="MaterialDrawerTheme.Light">
|
||||
@ -69,6 +74,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarGreyOrangeDark" parent="MaterialDrawerTheme">
|
||||
@ -81,6 +87,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarIndigoPink" parent="MaterialDrawerTheme.Light">
|
||||
@ -92,6 +99,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarIndigoPinkDark" parent="MaterialDrawerTheme">
|
||||
@ -104,6 +112,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarRedTeal" parent="MaterialDrawerTheme.Light">
|
||||
@ -115,6 +124,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarRedTealDark" parent="MaterialDrawerTheme">
|
||||
@ -127,6 +137,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarCyanPink" parent="MaterialDrawerTheme.Light">
|
||||
@ -138,6 +149,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarCyanPinkDark" parent="MaterialDrawerTheme">
|
||||
@ -150,6 +162,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
|
||||
@ -162,6 +175,7 @@
|
||||
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
<style name="NoBarTealOrangeDark" parent="MaterialDrawerTheme">
|
||||
@ -174,6 +188,7 @@
|
||||
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -1,10 +1,30 @@
|
||||
<?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"
|
||||
android:summaryOff="@string/login_debug_off"
|
||||
android:summaryOn="@string/login_debug_on"
|
||||
android:title="@string/login_debug_title" />
|
||||
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="read_debug"
|
||||
android:summaryOff="@string/read_debug_off"
|
||||
android:summaryOn="@string/read_debug_on"
|
||||
android:title="@string/read_debug_title" />
|
||||
|
||||
<Preference
|
||||
android:enabled="true"
|
||||
android:key="debug_identifier"
|
||||
android:summary="@string/summary_debug_identifier" />
|
||||
</PreferenceScreen>
|
@ -1,5 +1,4 @@
|
||||
<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!--<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
@ -18,6 +17,11 @@
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
android:title="@string/pref_api_items_number_title" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="infinite_loading"
|
||||
android:title="@string/pref_general_infinite_loading_title" />
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_general_category_links">
|
||||
|
||||
@ -39,6 +43,11 @@
|
||||
android:title="@string/pref_general_category_displaying">
|
||||
|
||||
</PreferenceCategory>
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="account_header_displaying"
|
||||
android:summary="@string/display_header_drawer_summary"
|
||||
android:title="@string/display_header_drawer_title" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="card_view_active"
|
||||
|
@ -7,4 +7,7 @@
|
||||
|
||||
<Preference android:title="@string/source_code"
|
||||
android:key="sourceLink" />
|
||||
|
||||
<Preference android:title="@string/translation"
|
||||
android:key="translation" />
|
||||
</PreferenceScreen>
|
12
build.gradle
12
build.gradle
@ -1,18 +1,24 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.3-2'
|
||||
ext.kotlin_version = '1.1.4-3'
|
||||
repositories {
|
||||
maven { url 'https://maven.google.com' }
|
||||
jcenter()
|
||||
google()
|
||||
maven {
|
||||
url "https://jitpack.io"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-beta2'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-rc2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.google.gms:google-services:3.1.0'
|
||||
|
||||
// Not the official version https://stackoverflow.com/questions/46525040/sonarqube-android-not-working-for-gradle-3-0-0/46813528#46813528
|
||||
classpath "com.github.Shusshu:sonar-scanner-gradle:feature~support-android-gradle-3-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
|
||||
|
5
crowdin.yml
Normal file
5
crowdin.yml
Normal file
@ -0,0 +1,5 @@
|
||||
files:
|
||||
- source: /app/src/main/res/values/strings.xml
|
||||
translation: /app/src/main/res/values-%two_letters_code%/%original_file_name%
|
||||
translate_attributes: '0'
|
||||
content_segmentation: '0'
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Wed Aug 16 19:45:22 CEST 2017
|
||||
#Sat Sep 02 11:43:17 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-rc-1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
BIN
secrets.tar.enc
BIN
secrets.tar.enc
Binary file not shown.
Reference in New Issue
Block a user