Merge pull request #808 from emasty/fast_actions_detail_view

Fast actions detail view
This commit is contained in:
David Luhmer 2020-05-07 19:39:10 +02:00 committed by GitHub
commit 3b56c86f84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 677 additions and 41 deletions

View file

@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Animatable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -35,11 +36,13 @@ import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ProgressBar;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@ -84,6 +87,14 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
// protected @BindView(R.id.bottomAppBar) BottomAppBar bottomAppBar;
protected @BindView(R.id.progressIndicator) ProgressBar progressIndicator;
//protected @BindView(R.id.btn_disable_incognito) ImageButton mBtnDisableIncognito;
protected @BindView(R.id.fa_detail_bar) View fastActionDetailBar;
protected @BindView(R.id.fa_collapse_layout) View fastActionCollapseLayout;
protected @BindView(R.id.fa_star) AppCompatImageButton fastActionStar;
protected @BindView(R.id.fa_mark_as_read) AppCompatImageButton fastActionRead;
protected @BindView(R.id.fa_toggle) AppCompatImageButton fastActionToggle;
protected @BindView(R.id.fa_open_in_browser) AppCompatImageButton fastActionOpenInBrowser;
protected @BindView(R.id.fa_share) AppCompatImageButton fastActionShare;
/**
* The {@link ViewPager} that will host the section contents.
@ -224,6 +235,7 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
}
mViewPager.addOnPageChangeListener(onPageChangeListener);
this.initFastActionBar();
/*
mBtnDisableIncognito.setOnClickListener(v -> {
@ -232,6 +244,67 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
*/
}
@Override
protected void onResume() {
super.onResume();
updateActionBarIcons();
}
/**
* Init fast action bar based on user settings.
* Only show if user selected setting CB_SHOW_FAST_ACTIONS. Otherwise hide.
*
* author: emasty https://github.com/emasty
*/
private void initFastActionBar() {
boolean showFastActions = mPrefs.getBoolean(SettingsActivity.CB_SHOW_FAST_ACTIONS, true);
if (showFastActions) {
// Set click listener for buttons on action bar
fastActionOpenInBrowser.setOnClickListener(v -> this.openInBrowser(currentPosition));
fastActionShare.setOnClickListener(v -> this.share(currentPosition));
fastActionToggle.setOnClickListener(v -> this.toggleFastActionBar());
RssItem rssItem = rssItems.get(currentPosition);
boolean isStarred = rssItem.getStarred_temp();
boolean isRead = rssItem.getRead_temp();
fastActionStar.setOnClickListener(v -> NewsDetailActivity.this.toggleRssItemStarredState());
fastActionStar.setImageResource(isStarred ? R.drawable.ic_action_star_dark : R.drawable.ic_action_star_border_dark);
fastActionRead.setOnClickListener(v -> NewsDetailActivity.this.markRead(currentPosition));
fastActionRead.setImageResource(isRead ? R.drawable.ic_check_box_white : R.drawable.ic_check_box_outline_blank_white);
fastActionDetailBar.setVisibility(View.VISIBLE);
} else {
fastActionDetailBar.setVisibility(View.INVISIBLE);
}
}
/**
* Expands or shrinks the fast action bar to show/hide secondary functions
*/
private void toggleFastActionBar() {
int currentState = fastActionCollapseLayout.getVisibility();
switch (currentState) {
case View.GONE:
fastActionToggle.setImageResource(R.drawable.ic_fa_expand);
fastActionCollapseLayout.setVisibility(View.VISIBLE);
break;
case View.VISIBLE:
fastActionToggle.setImageResource(R.drawable.ic_fa_shrink);
fastActionCollapseLayout.setVisibility(View.GONE);
break;
default:
break;
}
//((Animatable)fastActionToggle.getDrawable()).start();
fastActionToggle.setScaleX(-1);
}
private void toggleIncognitoMode() {
// toggle incognito mode
setIncognitoEnabled(!isIncognitoEnabled());
@ -281,7 +354,7 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
public static SORT_DIRECTION getSortDirectionFromSettings(SharedPreferences prefs) {
SORT_DIRECTION sDirection = SORT_DIRECTION.asc;
String sortDirection = prefs.getString(SettingsActivity.SP_SORT_ORDER, "1");
if (sortDirection.equals("1"))
if ("1".equals(sortDirection))
sDirection = SORT_DIRECTION.desc;
return sDirection;
}
@ -355,8 +428,7 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
mPostDelayHandler.delayTimer();
Log.v("PAGE CHANGED", "PAGE: " + position + " - IDFEED: " + rssItems.get(position).getId());
}
else {
} else {
updateActionBarIcons();
}
}
@ -396,20 +468,27 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
menuItem_PlayPodcast.setVisible(podcastAvailable);
}
if(menuItem_Starred != null) {
if (isStarred) {
menuItem_Starred.setIcon(R.drawable.ic_action_star_dark);
fastActionStar.setImageResource(R.drawable.ic_action_star_dark);
} else {
menuItem_Starred.setIcon(R.drawable.ic_action_star_border_dark);
fastActionStar.setImageResource(R.drawable.ic_action_star_border_dark);
}
}
if(isStarred && menuItem_Starred != null)
menuItem_Starred.setIcon(R.drawable.ic_action_star_dark);
else if(menuItem_Starred != null)
menuItem_Starred.setIcon(R.drawable.ic_action_star_border_dark);
if(isRead && menuItem_Read != null) {
menuItem_Read.setIcon(R.drawable.ic_check_box_white);
menuItem_Read.setChecked(true);
}
else if(menuItem_Read != null) {
menuItem_Read.setIcon(R.drawable.ic_check_box_outline_blank_white);
menuItem_Read.setChecked(false);
}
if(menuItem_Read != null) {
if (isRead) {
menuItem_Read.setIcon(R.drawable.ic_check_box_white);
menuItem_Read.setChecked(true);
fastActionRead.setImageResource(R.drawable.ic_check_box_white);
} else {
menuItem_Read.setIcon(R.drawable.ic_check_box_outline_blank_white);
menuItem_Read.setChecked(false);
fastActionRead.setImageResource(R.drawable.ic_check_box_outline_blank_white);
}
}
}

View file

@ -182,10 +182,9 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
mProgressBarLoading.setVisibility(View.GONE);
// Make sure to sync the incognitio on retained views
syncIncognitoState();
this.addBottomPaddingForFastActions(mWebView);
}
setUpGestureDetector();
return rootView;
@ -339,8 +338,8 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
addBottomPaddingForFastActions(view);
}
});
mWebView.setOnTouchListener((v, event) -> {
@ -356,6 +355,30 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
});
}
/**
* Add free space to bottom of web-site if Fast-Actions are switched on.
* Otherwise the fast action bar might hide the article content.
* Method to modify the body margins with JavaScript seems to be dirty, but no other
* solution seems to be available.
*
* This method does (for unknown reasons) not work if WebView gets restored. The Javascript is
* called but not executed.
*
* This is (only) a problem, if user swipes back in viewpager to already loaded articles.
* Solution might be to switch to a different design.
* - Bottom App Bar -- overall cleanest solution but interferes with current implementation
* of Podcast Player
* - Auto-hiding ActionBar. Hard to implement as scroll behaviour of WebView has to be used
* for hiding/showing ActionBar.
*
* @param view WebView with article
*/
private void addBottomPaddingForFastActions(WebView view) {
if (mPrefs.getBoolean(SettingsActivity.CB_SHOW_FAST_ACTIONS,true)) {
view.loadUrl("javascript:document.body.style.marginBottom=\"100px\"; void 0");
}
}
/**
* Loads the given url in the selected view based on user settings (Custom Chrome Tabs, webview or external)
*

View file

@ -27,6 +27,7 @@ import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
@ -40,6 +41,9 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
@ -52,6 +56,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
import javax.inject.Inject;
@ -202,6 +208,7 @@ public class NewsReaderDetailFragment extends Fragment {
Log.v(TAG, "onResume called!");
mMarkAsReadWhileScrollingEnabled = mPrefs.getBoolean(SettingsActivity.CB_MARK_AS_READ_WHILE_SCROLLING_STRING, false);
this.initFastDoneAll(this.getView());
//When the fragment is instantiated by the xml file, onResume will be called twice
if (onResumeCount >= 2) {
@ -251,6 +258,21 @@ public class NewsReaderDetailFragment extends Fragment {
}
}
/**
* Init fast action for mark all as read shown as floating action bar button (fab)
*
* @param rootView root view of fragment
*/
protected void initFastDoneAll(View rootView) {
FloatingActionButton fab_done_all = rootView.findViewById(R.id.fab_done_all);
if (mPrefs.getBoolean(SettingsActivity.CB_SHOW_FAST_ACTIONS, true)) {
fab_done_all.setVisibility(View.VISIBLE);
fab_done_all.setOnTouchListener(new FastMarkReadMotionListener(rootView));
} else {
fab_done_all.setVisibility(View.GONE);
}
}
/**
* Updates the current RSS-View
*/
@ -654,5 +676,160 @@ public class NewsReaderDetailFragment extends Fragment {
}
}
/**
* MotionListener for Floating Action Bar Button to mark all articles in current
* news feed as marked without using the menu.
*
* A movement up is required to prevent accidentally marking articles as read.
*/
private class FastMarkReadMotionListener implements View.OnTouchListener {
private View fabMarkAllAsRead;
private ImageView targetView;
private boolean markAsRead = false;
private float originX,
originY;
private float dx,
dy;
public FastMarkReadMotionListener(View fabMarkAllAsRead) {
this.fabMarkAllAsRead = fabMarkAllAsRead;
this.targetView = (ImageView)fabMarkAllAsRead.findViewById(R.id.target_done_all);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.startUserInteractionProcess(v, event);
break;
case MotionEvent.ACTION_MOVE:
this.moveFAB(v, event);
break;
case MotionEvent.ACTION_UP:
this.stopUserInteractionProcess(v);
break;
default:
// Do nothing
break;
}
return true;
}
/**
* Start Animation for user to drag all read button to target.
* Once the button is moved to the target, a success animation is loaded and shown.
*
* @param v FAB moved by the user
* @param event motion event for v
*/
private void startUserInteractionProcess(View v, MotionEvent event) {
// Save start location of movement and button
this.originX = v.getX();
this.originY = v.getY();
this.dx = v.getX() - event.getRawX();
this.dy = v.getY() - event.getRawY();
this.markAsRead = false;
// Start animation of target
this.targetView.setImageResource(R.drawable.fa_all_read_target);
this.targetView.setVisibility(View.VISIBLE);
((Animatable)this.targetView.getDrawable()).start();
}
/**
* Handle move event of FAB to mark all articles as read
* Two things are done here:
* - button location is changed
* - it is checked iv button is moved into target area
*
* @param v FAB moved by the user
* @param event motion event for v
*/
private void moveFAB(View v, MotionEvent event) {
v.setX(event.getRawX() + this.dx);
v.setY(event.getRawY() + this.dy);
this.checkLocation(event);
}
/**
* Checks if FAB to mark all as read was moved within the shown target area.
* For location calculation, the actual location of the target view is read
* and calculated if current move position is within the view area of the target view.
*
* @param evt MotionEvent of all read FAB
*/
private void checkLocation(MotionEvent evt) {
// Location on screen for target is required as motion event returns location on screen
int[] location = new int[2];
this.targetView.getLocationOnScreen(location);
Rect r = new Rect(location[0], location[1],
(location[0] + targetView.getWidth()),
(location[1] + targetView.getHeight()));
if (r.contains((int)evt.getRawX(), (int)evt.getRawY())) {
if (!this.markAsRead) {
this.markAsRead = true;
this.targetView.setImageResource(R.drawable.fa_all_read_target_success);
((Animatable) this.targetView.getDrawable()).start();
}
} else {
if (this.markAsRead) {
this.markAsRead = false;
this.targetView.setImageResource(R.drawable.fa_all_read_target);
((Animatable) this.targetView.getDrawable()).start();
}
}
}
/**
* Stops the user interaction
* - FAB is animated back to original position
* - A success animation is shown of all articles will be marked as read
* - Target view is hidden again
*
* @param v view of fab
*/
private void stopUserInteractionProcess(View v) {
if (this.markAsRead) {
Animation anim_success = AnimationUtils.loadAnimation(NewsReaderDetailFragment.this.getContext(),
R.anim.all_read_success);
anim_success.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
v.animate().x(originX).y(originY).setDuration(100).setStartDelay(0).start();
}
@Override
public void onAnimationEnd(Animation animation) {
((Animatable)targetView.getDrawable()).stop();
targetView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
//Nothing to do here for now
}
});
this.targetView.startAnimation(anim_success);
this.markAllAsReadForCurrentView();
} else {
this.targetView.setVisibility(View.INVISIBLE);
v.animate().x(this.originX).y(this.originY).setDuration(100).setStartDelay(0).start();
((Animatable)this.targetView.getDrawable()).stop();
}
}
/**
* Mark all articles in current view as read.
*/
private void markAllAsReadForCurrentView() {
DatabaseConnectionOrm dbConn2 = new DatabaseConnectionOrm(this.fabMarkAllAsRead.getContext());
dbConn2.markAllItemsAsReadForCurrentView();
NewsReaderDetailFragment.this.refreshCurrentRssView();
}
}
}

View file

@ -72,6 +72,7 @@ public class SettingsActivity extends AppCompatActivity {
public static final String LV_CACHE_IMAGES_OFFLINE_STRING = "lv_cacheImagesOffline";
public static final String CB_MARK_AS_READ_WHILE_SCROLLING_STRING = "cb_MarkAsReadWhileScrolling";
public static final String CB_SHOW_FAST_ACTIONS = "cb_ShowFastActions";
public static final String CB_DISABLE_HOSTNAME_VERIFICATION_STRING = "cb_DisableHostnameVerification";
public static final String CB_SKIP_DETAILVIEW_AND_OPEN_BROWSER_DIRECTLY_STRING = "cb_openInBrowserDirectly";
public static final String CB_SHOW_NOTIFICATION_NEW_ARTICLES_STRING = "cb_showNotificationNewArticles";

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="4.0"
android:toYScale="4.0"
android:pivotX="100%"
android:pivotY="0%"
android:duration="300" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="300"
/>
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item android:drawable="@drawable/ic_fa_main_bg" />
</ripple>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -0,0 +1,54 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt" >
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group
android:name="scaleGroup"
android:pivotX="64"
android:pivotY="64"
android:scaleX="0.5"
android:scaleY="0.5"
>
<path
android:pathData="M64.001,64m-60,0a60,60 0,1 1,119.999 0a60,60 0,1 1,-119.999 0"
android:strokeWidth="6"
android:fillColor="#00000000"
android:strokeColor="#68BDEC"/>
</group>
<path
android:pathData="M64,64m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
android:fillColor="#68BDEC"
/>
<path
android:pathData="M68.969,58.656C68.603,58.291 68.012,58.291 67.647,58.656L62.359,63.944L63.681,65.266L68.969,59.969C69.325,59.612 69.325,59.013 68.969,58.656ZM72.944,58.647L63.681,67.909L60.419,64.656C60.053,64.291 59.463,64.291 59.097,64.656C58.731,65.022 58.731,65.613 59.097,65.978L63.016,69.897C63.381,70.262 63.972,70.262 64.338,69.897L74.266,59.978C74.631,59.612 74.631,59.022 74.266,58.656L74.256,58.656C73.9,58.281 73.309,58.281 72.944,58.647ZM53.8,65.988L57.719,69.906C58.084,70.272 58.675,70.272 59.041,69.906L59.697,69.25L55.122,64.656C54.756,64.291 54.166,64.291 53.8,64.656C53.434,65.022 53.434,65.622 53.8,65.988Z"
android:fillColor="#ffffff"
android:fillType="nonZero"/>
</vector>
</aapt:attr>
<target android:name="scaleGroup"> *
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="1.0"
android:valueTo="0.5" />
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="1.0"
android:valueTo="0.5" />
</set>
</aapt:attr>
</target>
</animated-vector>

View file

@ -0,0 +1,54 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt" >
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group
android:name="scaleGroup"
android:pivotX="64"
android:pivotY="64"
android:scaleX="0.5"
android:scaleY="0.5"
>
<path
android:pathData="M64.001,64m-60,0a60,60 0,1 1,119.999 0a60,60 0,1 1,-119.999 0"
android:strokeWidth="6"
android:fillColor="#00000000"
android:strokeColor="#007C1F"/>
</group>
<path
android:pathData="M64,64m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
android:fillColor="#007C1F"
/>
<path
android:pathData="M68.969,58.656C68.603,58.291 68.012,58.291 67.647,58.656L62.359,63.944L63.681,65.266L68.969,59.969C69.325,59.612 69.325,59.013 68.969,58.656ZM72.944,58.647L63.681,67.909L60.419,64.656C60.053,64.291 59.463,64.291 59.097,64.656C58.731,65.022 58.731,65.613 59.097,65.978L63.016,69.897C63.381,70.262 63.972,70.262 64.338,69.897L74.266,59.978C74.631,59.612 74.631,59.022 74.266,58.656L74.256,58.656C73.9,58.281 73.309,58.281 72.944,58.647ZM53.8,65.988L57.719,69.906C58.084,70.272 58.675,70.272 59.041,69.906L59.697,69.25L55.122,64.656C54.756,64.291 54.166,64.291 53.8,64.656C53.434,65.022 53.434,65.622 53.8,65.988Z"
android:fillColor="#ffffff"
android:fillType="nonZero"/>
</vector>
</aapt:attr>
<target android:name="scaleGroup"> *
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="0.5"
android:valueTo="1.0" />
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="0.5"
android:valueTo="1.0" />
</set>
</aapt:attr>
</target>
</animated-vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/colorPrimaryDark"/>
<item android:drawable="@color/colorPrimary"/>
</selector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
</vector>

View file

@ -0,0 +1,18 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M24.0001,24.0001m-21.0004,0a21.0004,21.0004 0,1 1,42.0009 0a21.0004,21.0004 0,1 1,-42.0009 0"
android:fillColor="#ffffff"
android:fillAlpha="0.25"/>
<path
android:name="path_1"
android:pathData="M 28 16 L 20 24 L 28 32 L 20 24 Z"
android:fillAlpha="0"
android:strokeColor="#ffffff"
android:strokeWidth="4"/>
</vector>

View file

@ -0,0 +1,35 @@
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M24.0001,24.0001m-21.0004,0a21.0004,21.0004 0,1 1,42.0009 0a21.0004,21.0004 0,1 1,-42.0009 0"
android:fillColor="#ffffff"
android:fillAlpha="0.25"/>
<path
android:name="path_1"
android:pathData="M 28 16 L 20 24 L 28 32 L 20 24 Z"
android:fillAlpha="0"
android:strokeColor="#ffffff"
android:strokeWidth="4"/>
</vector>
</aapt:attr>
<target android:name="path_1">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="pathData"
android:startOffset="1"
android:duration="300"
android:valueFrom="M 28 16 L 20 24 L 28 32 L 20 24 Z"
android:valueTo="M 20 16 L 28 24 L 20 32 L 28 24 Z"
android:valueType="pathType"
android:interpolator="@android:interpolator/linear"/>
</aapt:attr>
</target>
</animated-vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56dp"
android:height="56dp"
android:viewportWidth="56"
android:viewportHeight="56">
<path
android:pathData="M0,0L28,0C43.454,0 56,12.546 56,28C56,43.393 43.551,55.902 28.181,55.999L28,56L0,56L0,0Z"
android:fillColor="#0082C9"/>
</vector>

View file

@ -0,0 +1,36 @@
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M24.0001,24.0001m-21.0004,0a21.0004,21.0004 0,1 1,42.0009 0a21.0004,21.0004 0,1 1,-42.0009 0"
android:fillColor="#ffffff"
android:fillAlpha="0.25"/>
<path
android:name="path_1"
android:pathData="M 20 16 L 28 24 L 20 32 L 28 24 Z"
android:fillAlpha="0"
android:strokeColor="#ffffff"
android:strokeWidth="4"/>
</vector>
</aapt:attr>
<target android:name="path_1">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="pathData"
android:startOffset="1"
android:duration="300"
android:valueFrom="M 20 16 L 28 24 L 20 32 L 28 24 Z"
android:valueTo="M 28 16 L 20 24 L 28 32 L 20 24 Z"
android:valueType="pathType"
android:interpolator="@android:interpolator/linear"/>
</aapt:attr>
</target>
</animated-vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View file

@ -21,30 +21,46 @@
for bottom toolbar, replace marginTop above with marginBottom
android:layout_marginBottom="?attr/actionBarSize"
-->
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NewsDetailActivity" >
<!--
This title strip will display the currently visible page title, as well as the page
titles for adjacent pages.
-->
<!--
<android.support.v4.view.PagerTitleStrip
android:id="@+id/pager_title_strip"
>
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NewsDetailActivity" >
<!--
This title strip will display the currently visible page title, as well as the page
titles for adjacent pages.
-->
<!--
<android.support.v4.view.PagerTitleStrip
android:id="@+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
-->
</androidx.viewpager.widget.ViewPager>
<include
android:id="@+id/fa_detail_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
-->
</androidx.viewpager.widget.ViewPager>
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="11dp"
android:layout_marginEnd="11dp"
layout="@layout/widget_fastactions_detailview" />
</RelativeLayout>
<FrameLayout
android:id="@+id/podcast_frame"
@ -71,8 +87,7 @@
android:theme="@style/ToolbarTheme"
app:popupTheme="@style/ToolbarOptionMenuBackgroundTheme">
</com.google.android.material.bottomappbar.BottomAppBar>
-->
-->
<include
android:id="@+id/toolbar_layout"

View file

@ -1,4 +1,5 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -37,6 +38,41 @@
android:indeterminate="true" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_done_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:visibility="visible"
android:translationX="-16dp"
android:translationY="-16dp"
app:tint="@android:color/white"
app:fabSize="normal"
app:backgroundTint="@color/colorPrimary"
app:srcCompat="@drawable/ic_done_all"
/>
<ImageView
android:id="@+id/target_done_all"
android:layout_width="128dp"
android:layout_height="128dp"
android:translationX="18dp"
android:translationY="-64dp"
android:layout_above="@id/fab_done_all"
android:layout_alignParentEnd="true"
app:srcCompat="@drawable/fa_all_read_target"
android:visibility="invisible" />
</RelativeLayout>
<!-- android:textIsSelectable="true" -->
<!-- android:padding="16dp" -->

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/fa_bg"
android:orientation="horizontal"
android:animateLayoutChanges="true">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fa_toggle"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="@color/transparent"
app:srcCompat="@drawable/ic_fa_expand" />
<LinearLayout
android:id="@+id/fa_collapse_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/fa_toggle"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fa_mark_as_read"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_check_box_outline_blank_white" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fa_star"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_action_star_border_dark" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fa_share"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_share_white" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/fa_open_in_browser"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_toEndOf="@id/fa_collapse_layout"
android:background="@drawable/fa_button"
android:src="@drawable/ic_action_open_in_browser"
android:translationZ="50dp" />
</RelativeLayout>

View file

@ -144,6 +144,7 @@
<string name="pref_title_DisableHostnameVerification">Hostnamen-Nachweis deaktivieren</string>
<string name="pref_title_NavigateWithVolumeButtons">Mit Lautstärketasten navigieren</string>
<string name="pref_title_MarkAsReadWhileScrolling">Beim Bildlauf als gelesen markieren</string>
<string name="pref_title_ShowFastActions">Schnellzugriffe aktivieren</string>
<string name="pref_title_OpenInBrowserDirectly">Detailansicht überspringen und Artikel in Browser öffnen</string>
<string name="dialog_feature_not_available">Diese Funktion ist in dieser (Open-Source-)Version dieser App nicht verfügbar. Wenn Sie diese Funktion nutzen möchten, laden Sie bitte die App aus dem GitHub-Repository oder aus dem Google Play Store herunter.</string>

View file

@ -180,6 +180,7 @@
<string name="pref_title_DisableHostnameVerification">Disable Hostname Verification</string>
<string name="pref_title_NavigateWithVolumeButtons">Navigate with volume buttons</string>
<string name="pref_title_MarkAsReadWhileScrolling">Mark as read while scrolling</string>
<string name="pref_title_ShowFastActions">Activate fast access functions</string>
<string name="pref_title_OpenInBrowserDirectly">Skip detailed view and open article in the browser</string>
<string name="dialog_feature_not_available">This feature is not available in this (open-source) version of this app. If you want to use this feature please download the app from the GitHub Repository or download the App from the Google Play Store.</string>

View file

@ -78,6 +78,11 @@
android:title="@string/pref_title_MarkAsReadWhileScrolling"
app:iconSpaceReserved="false"/>
<SwitchPreference
android:key="cb_ShowFastActions"
android:title="@string/pref_title_ShowFastActions"
app:iconSpaceReserved="false"/>
<SwitchPreference
android:key="cb_openInBrowserDirectly"
android:title="@string/pref_title_OpenInBrowserDirectly"