Compare commits

...

7 Commits

Author SHA1 Message Date
9d40026ef7 Build and changeelog. 2017-06-04 09:47:45 +02:00
041a225992 Updated Custom tab code and added buttons at the bottom of the article viewer. 2017-06-03 22:52:01 +02:00
b42dc7f87c Update README.md 2017-05-30 20:37:27 +02:00
0283e49c27 Testing circle ci. 2017-05-30 20:35:01 +02:00
e5e1b2f5a5 Update README.md 2017-05-29 19:42:38 +02:00
813a0ae475 Update README.md 2017-05-29 19:13:42 +02:00
f0e036cdd8 Update README.md 2017-05-28 18:31:39 +02:00
11 changed files with 281 additions and 85 deletions

View File

@ -1,3 +1,11 @@
**1.5.0.2**
- If the content in the article viewer is empty, the article will open in a custom tab.
- Added a share button, and an "open in browser" button to the bottom of the article viewer.
- Updated custom tab code.
**1.5.0.1**
- The release APK wasn't working at all.

View File

@ -1,5 +1,7 @@
# ReaderForSelfoss
[![CircleCI](https://circleci.com/gh/aminecmi/ReaderforSelfoss/tree/master.svg?style=svg)](https://circleci.com/gh/aminecmi/ReaderforSelfoss/tree/master)
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/)
@ -26,4 +28,3 @@ You'll have to:
- [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)
- [Help me translate the app](https://poeditor.com/join/project/viHr8ujJ7S)

View File

@ -25,8 +25,8 @@ android {
applicationId "apps.amine.bou.readerforselfoss"
minSdkVersion 16
targetSdkVersion 25
versionCode 1501
versionName "1.5.0.1"
versionCode 1502
versionName "1.5.0.2"
// Enabling multidex support.
multiDexEnabled true
@ -115,10 +115,20 @@ dependencies {
}
apply plugin: 'com.google.gms.google-services'
afterEvaluate {
initFabricPropertiesIfNeeded()
}
apply plugin: 'com.google.gms.google-services'
def initFabricPropertiesIfNeeded() {
def propertiesFile = file('fabric.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated fabric property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: "fabric.properties", comment: commentMessage) {
entry(key: "apiSecret", value: crashlyticsdemoApisecret)
entry(key: "apiKey", value: crashlyticsdemoApikey)
}
}
}

View File

@ -6,6 +6,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
@ -44,6 +45,9 @@ class ReaderActivity : DragDismissActivity() {
val content = v.findViewById(R.id.content) as HtmlTextView
val url = intent.getStringExtra("url")
val parser = MercuryApi(getString(R.string.mercury))
val browserBtn: ImageButton = v.findViewById(R.id.browserBtn) as ImageButton
val shareBtn: ImageButton = v.findViewById(R.id.shareBtn) as ImageButton
val customTabsIntent = buildCustomTabsIntent(this@ReaderActivity)
mCustomTabActivityHelper = CustomTabActivityHelper()
@ -52,13 +56,30 @@ class ReaderActivity : DragDismissActivity() {
parser.parseUrl(url).enqueue(object : Callback<ParsedContent> {
override fun onResponse(call: Call<ParsedContent>, response: Response<ParsedContent>) {
if (response.body() != null) {
if (response.body() != null && response.body()!!.content.isNotEmpty()) {
source.text = response.body()!!.domain
title.text = response.body()!!.title
if (response.body()!!.content != null && !response.body()!!.content.isEmpty())
content.setHtml(response.body()!!.content, HtmlHttpImageGetter(content, null, true))
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isEmpty())
Glide.with(applicationContext).load(response.body()!!.lead_image_url).asBitmap().fitCenter().into(image)
shareBtn.setOnClickListener {
val sendIntent = Intent()
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT, response.body()!!.url)
sendIntent.type = "text/plain"
startActivity(Intent.createChooser(sendIntent, getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
browserBtn.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(response.body()!!.url)
startActivity(intent)
}
hideProgressBar()
} else {
errorAfterMercuryCall()

View File

@ -1,7 +1,6 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.app.Activity;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsClient;
@ -11,28 +10,30 @@ import android.support.customtabs.CustomTabsSession;
import java.util.List;
@SuppressWarnings("ALL")
public class CustomTabActivityHelper {
/**
* This is a helper class to manage the connection to the Custom Tabs Service.
*/
public class CustomTabActivityHelper implements ServiceConnectionCallback {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private ConnectionCallback mConnectionCallback;
/**
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
*
* @param activity The host activity
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available
* @param uri the Uri to be opened
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available
* @param activity The host activity.
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
* @param uri the Uri to be opened.
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
*/
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means there's no browser that supports
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
@ -45,22 +46,21 @@ public class CustomTabActivityHelper {
}
/**
* Unbinds the Activity from the Custom Tabs Service
* @param activity the activity that is connected to the service
* Unbinds the Activity from the Custom Tabs Service.
* @param activity the activity that is connected to the service.
*/
public void unbindCustomTabsService(Activity activity) {
try {
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
} catch (RuntimeException e) {}
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
mConnection = null;
}
/**
* Creates or retrieves an exiting CustomTabsSession
* Creates or retrieves an exiting CustomTabsSession.
*
* @return a CustomTabsSession
* @return a CustomTabsSession.
*/
public CustomTabsSession getSession() {
if (mClient == null) {
@ -72,7 +72,7 @@ public class CustomTabActivityHelper {
}
/**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service.
* @param connectionCallback
*/
public void setConnectionCallback(ConnectionCallback connectionCallback) {
@ -80,67 +80,72 @@ public class CustomTabActivityHelper {
}
/**
* Binds the Activity to the Custom Tabs Service
* @param activity the activity to be binded to the service
* Binds the Activity to the Custom Tabs Service.
* @param activity the activity to be binded to the service.
*/
public void bindCustomTabsService(Activity activity) {
if (mClient != null) return;
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
if (packageName == null) return;
mConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
//Initialize a session as soon as possible.
getSession();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mClient = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
};
mConnection = new ServiceConnection(this);
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}
/**
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
* @return true if call to mayLaunchUrl was accepted.
*/
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
if (mClient == null) return false;
CustomTabsSession session = getSession();
return session != null && session.mayLaunchUrl(uri, extras, otherLikelyBundles);
if (session == null) return false;
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
}
@Override
public void onServiceConnected(CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
}
@Override
public void onServiceDisconnected() {
mClient = null;
mCustomTabsSession = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
/**
* A Callback for when the service is connected or disconnected. Use those callbacks to
* handle UI changes when the service is connected or disconnected
* handle UI changes when the service is connected or disconnected.
*/
public interface ConnectionCallback {
/**
* Called when the service is connected
* Called when the service is connected.
*/
void onCustomTabsConnected();
/**
* Called when the service is disconnected
* Called when the service is disconnected.
*/
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available
* To be used as a fallback to open the Uri when Custom Tabs is not available.
*/
public interface CustomTabFallback {
/**
*
* @param activity The Activity that wants to open the Uri
* @param uri The uri to be opened by the fallback
* @param activity The Activity that wants to open the Uri.
* @param uri The uri to be opened by the fallback.
*/
void openUri(Activity activity, Uri uri);
}
}
}

View File

@ -0,0 +1,32 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.content.ComponentName;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsServiceConnection;
import java.lang.ref.WeakReference;
/**
* Implementation for the CustomTabsServiceConnection that avoids leaking the
* ServiceConnectionCallback
*/
public class ServiceConnection extends CustomTabsServiceConnection {
// A weak reference to the ServiceConnectionCallback to avoid leaking it.
private WeakReference<ServiceConnectionCallback> mConnectionCallback;
public ServiceConnection(ServiceConnectionCallback connectionCallback) {
mConnectionCallback = new WeakReference<>(connectionCallback);
}
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceConnected(client);
}
@Override
public void onServiceDisconnected(ComponentName name) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceDisconnected();
}
}

View File

@ -0,0 +1,18 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.support.customtabs.CustomTabsClient;
public interface ServiceConnectionCallback {
/**
* Called when the service is connected.
* @param client a CustomTabsClient
*/
void onServiceConnected(CustomTabsClient client);
/**
* Called when the service is disconnected.
*/
void onServiceDisconnected();
}

View File

@ -62,5 +62,64 @@
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Some text @android:string/fingerprint_icon_content_description" />
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="0dp"
android:layout_marginEnd="0dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:background="#BBBBBB"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/content"
app:layout_constraintVertical_bias="1.0">
<ImageButton
android:id="@+id/browserBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_open_in_browser_black_24dp"
android:tint="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/shareBtn"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/shareBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_share_black_24dp"
android:tint="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/browserBtn"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

View File

@ -54,65 +54,64 @@
android:gravity="start" />
<RelativeLayout
android:id="@+id/actionBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#BBBBBB"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate"
android:id="@+id/actionBar"
android:background="#BBBBBB"
android:visibility="gone">
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
<com.like.LikeButton
app:icon_type="heart"
app:icon_size="22dp"
android:id="@+id/favButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:elevation="5dp"
android:padding="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"/>
app:icon_size="22dp"
app:icon_type="heart" />
<ImageButton
android:id="@+id/shareBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:src="@drawable/ic_share_black_24dp"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/favButton"
android:layout_toStartOf="@+id/favButton"
android:id="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:tint="#000000"
android:adjustViewBounds="true"
android:padding="4dp"
android:scaleType="centerCrop"
android:padding="4dp"/>
android:src="@drawable/ic_share_black_24dp"
android:tint="#000000" />
<ImageButton
android:id="@+id/browserBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:src="@drawable/ic_open_in_browser_black_24dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:id="@+id/browserBtn"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:elevation="5dp"
android:background="@android:color/transparent"
android:tint="#000000"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:padding="4dp"/>
android:src="@drawable/ic_open_in_browser_black_24dp"
android:tint="#000000" />
</RelativeLayout>

View File

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

43
circle.yml Normal file
View File

@ -0,0 +1,43 @@
machine:
timezone: Europe/Paris
java:
version: 'oraclejdk8'
environment:
ANDROID_HOME: /usr/local/android-sdk-linux
PATH: ANDROID_HOME:$ANDROID_HOME/platform-tools:$ANDROID_BUUILD:$ANDROID_HOME/tools:$PATH
_JAVA_OPTIONS: "-Xms512m -Xmx1024m"
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
dependencies:
pre:
- touch app/google-services.json
- echo $GOOGLE_SERVICES_JSON > app/google-services.json
- touch app/src/main/res/values/secrets.xml
- echo $SECRETS_XML > app/src/main/res/values/secrets.xml
- mkdir app/src/main/res/mipmap-hdpi
- convert -size 72x72 xc:white app/src/main/res/mipmap-hdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-mdpi
- convert -size 48x48 xc:white app/src/main/res/mipmap-mdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xhdpi
- convert -size 96x96 xc:white app/src/main/res/mipmap-xhdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xxhdpi
- convert -size 192x192 xc:white app/src/main/res/mipmap-xxhdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xxxhdpi
- convert -size 512x512 xc:white app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
cache_directories:
- ~/.gradle
- ~/.android
override:
- echo y | android update sdk --no-ui --filter "android-25,build-tools-25.0.3"
- echo y | android update sdk --no-ui --all --filter "platform-tools, tools"
- echo y | android update sdk --no-ui --all --filter "android-25, build-tools-25.0.3"
- echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
- echo y | android update sdk --no-ui --all --filter "extra-android-support"
- echo y | android update sdk --no-ui --all --filter "extra-google-m2repository"
- echo y | android update sdk --no-ui --all --filter "extra-google-google_play_services"
test:
override:
- gradlew assemble -P crashlyticsdemoApikey=$FABRIC_API_KEY -P crashlyticsdemoApisecret=$FABRIC_API_SECRET