improved keyboard shortcut - prev/next item

added better description for 'read from bottom' option, close #70.
This commit is contained in:
Mariotaku Lee 2015-04-23 13:22:48 +08:00
parent 39f55889fa
commit 19a91578da
5 changed files with 192 additions and 158 deletions

View File

@ -28,6 +28,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
@ -39,6 +40,7 @@ import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.ShareActionProvider;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -71,6 +73,7 @@ import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMedia.VideoInfo.Variant;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.SaveFileTask;
import org.mariotaku.twidere.util.ThemeUtils;
@ -86,19 +89,15 @@ import pl.droidsonroids.gif.GifTextureView;
import pl.droidsonroids.gif.InputSource.FileSource;
public final class MediaViewerActivity extends ThemedAppCompatActivity implements Constants, OnPageChangeListener {
public final class MediaViewerActivity extends BaseAppCompatActivity implements Constants, OnPageChangeListener {
private static final String EXTRA_LOOP = "loop";
private static boolean ANIMATED_GIF_SUPPORTED = GifSupportChecker.isSupported();
private ViewPager mViewPager;
private MediaPagerAdapter mAdapter;
private ActionBar mActionBar;
private MediaPagerAdapter mPagerAdapter;
private View mMediaStatusContainer;
private LinePageIndicator mIndicator;
private static boolean ANIMATED_GIF_SUPPORTED = GifSupportChecker.isSupported();
@Override
public int getThemeColor() {
return ThemeUtils.getUserAccentColor(this);
@ -109,10 +108,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
return ThemeUtils.getViewerThemeResource(this);
}
public boolean hasStatus() {
return getIntent().hasExtra(EXTRA_STATUS);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -124,37 +119,16 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
return super.onOptionsItemSelected(item);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
setBarVisibility(true);
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onContentChanged() {
super.onContentChanged();
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mIndicator = (LinePageIndicator) findViewById(R.id.pager_indicator);
mMediaStatusContainer = findViewById(R.id.media_status_container);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActionBar = getSupportActionBar();
mActionBar.setDisplayHomeAsUpEnabled(true);
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
setContentView(R.layout.activity_media_viewer);
mAdapter = new MediaPagerAdapter(this);
mViewPager.setAdapter(mAdapter);
mPagerAdapter = new MediaPagerAdapter(this);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setPageMargin(getResources().getDimensionPixelSize(R.dimen.element_spacing_normal));
mViewPager.setOnPageChangeListener(this);
mIndicator.setSelectedColor(getCurrentThemeColor());
@ -163,7 +137,7 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
final long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
final ParcelableMedia[] media = Utils.newParcelableArray(intent.getParcelableArrayExtra(EXTRA_MEDIA), ParcelableMedia.CREATOR);
final ParcelableMedia currentMedia = intent.getParcelableExtra(EXTRA_CURRENT_MEDIA);
mAdapter.setMedia(accountId, media);
mPagerAdapter.setMedia(accountId, media);
mIndicator.notifyDataSetChanged();
final int currentIndex = ArrayUtils.indexOf(media, currentMedia);
if (currentIndex != -1) {
@ -185,13 +159,71 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
}
}
public boolean hasStatus() {
return getIntent().hasExtra(EXTRA_STATUS);
}
@Override
public void onContentChanged() {
super.onContentChanged();
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mIndicator = (LinePageIndicator) findViewById(R.id.pager_indicator);
mMediaStatusContainer = findViewById(R.id.media_status_container);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
setBarVisibility(true);
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public boolean handleKeyboardShortcutSingle(@NonNull KeyboardShortcutsHandler handler, int keyCode, @NonNull KeyEvent event) {
final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event);
if (action != null) {
switch (action) {
case ACTION_NAVIGATION_PREVIOUS_TAB: {
final int previous = mViewPager.getCurrentItem() - 1;
if (previous < 0) {
} else if (previous < mPagerAdapter.getCount()) {
mViewPager.setCurrentItem(previous, true);
}
return true;
}
case ACTION_NAVIGATION_NEXT_TAB: {
final int next = mViewPager.getCurrentItem() + 1;
if (next >= mPagerAdapter.getCount()) {
} else if (next >= 0) {
mViewPager.setCurrentItem(next, true);
}
return true;
}
case ACTION_NAVIGATION_BACK: {
onBackPressed();
return true;
}
}
}
return super.handleKeyboardShortcutSingle(handler, keyCode, event);
}
private ParcelableStatus getStatus() {
return getIntent().getParcelableExtra(EXTRA_STATUS);
}
private boolean isBarShowing() {
if (mActionBar == null) return false;
return mActionBar.isShowing();
final ActionBar actionBar = getSupportActionBar();
if (actionBar == null) return false;
return actionBar.isShowing();
}
private boolean isMediaStatusEnabled() {
@ -199,11 +231,12 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
}
private void setBarVisibility(boolean visible) {
if (mActionBar == null) return;
final ActionBar actionBar = getSupportActionBar();
if (actionBar == null) return;
if (visible) {
mActionBar.show();
actionBar.show();
} else {
mActionBar.hide();
actionBar.hide();
}
mIndicator.setVisibility(visible ? View.VISIBLE : View.GONE);
@ -271,28 +304,10 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
invalidateOptionsMenu();
}
protected void setImageViewVisibility(int visible) {
mImageView.setVisibility(visible);
}
protected void setLoadProgress(float progress) {
mProgressBar.setProgress(progress);
}
protected void setLoadProgressVisibility(int visibility) {
mProgressBar.setVisibility(visibility);
}
@Override
public void onLoaderReset(final Loader<TileImageLoader.Result> loader) {
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_media_page_image_compat, container, false);
}
@Override
public void onDownloadError(final Throwable t) {
mContentLength = 0;
@ -320,29 +335,22 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
setLoadProgress(downloaded / mContentLength);
}
protected void setImageViewVisibility(int visible) {
mImageView.setVisibility(visible);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final File file = mImageFile;
final boolean isLoading = getLoaderManager().hasRunningLoaders();
final boolean hasImage = file != null && file.exists();
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasImage && !isLoading);
if (!hasImage) return;
final MenuItem shareItem = menu.findItem(R.id.share);
final ShareActionProvider shareProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
final Intent intent = new Intent(Intent.ACTION_SEND);
final Uri fileUri = Uri.fromFile(file);
intent.setDataAndType(fileUri, Utils.getImageMimeType(file));
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity.hasStatus()) {
final ParcelableStatus status = activity.getStatus();
intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
}
shareProvider.setShareIntent(intent);
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_media_page_image_compat, container, false);
}
protected void setLoadProgress(float progress) {
mProgressBar.setProgress(progress);
}
protected void setLoadProgressVisibility(int visibility) {
mProgressBar.setVisibility(visibility);
}
private ParcelableMedia getMedia() {
@ -372,11 +380,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
startActivity(intent);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_image_page, menu);
}
private void saveToGallery() {
if (mSaveFileTask != null && mSaveFileTask.getStatus() == Status.RUNNING) return;
final File file = mImageFile;
@ -386,6 +389,38 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
mSaveFileTask.execute();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final File file = mImageFile;
final boolean isLoading = getLoaderManager().hasRunningLoaders();
final boolean hasImage = file != null && file.exists();
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasImage && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasImage && !isLoading);
if (!hasImage) return;
final MenuItem shareItem = menu.findItem(R.id.share);
final ShareActionProvider shareProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
final Intent intent = new Intent(Intent.ACTION_SEND);
final Uri fileUri = Uri.fromFile(file);
intent.setDataAndType(fileUri, Utils.getImageMimeType(file));
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
if (activity.hasStatus()) {
final ParcelableStatus status = activity.getStatus();
intent.putExtra(Intent.EXTRA_TEXT, Utils.getStatusShareText(activity, status));
intent.putExtra(Intent.EXTRA_SUBJECT, Utils.getStatusShareSubject(activity, status));
}
shareProvider.setShareIntent(intent);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_image_page, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -560,14 +595,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
return true;
}
@Override
public void onBaseViewCreated(View view, Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mVideoView = (TextureVideoView) view.findViewById(R.id.video_view);
mVideoViewProgress = (ProgressBar) view.findViewById(R.id.video_view_progress);
mProgressBar = (ProgressWheel) view.findViewById(R.id.load_progress);
}
@Override
public void onPrepared(MediaPlayer mp) {
if (getUserVisibleHint()) {
@ -591,6 +618,14 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
invalidateOptionsMenu();
}
@Override
public void onBaseViewCreated(View view, Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mVideoView = (TextureVideoView) view.findViewById(R.id.video_view);
mVideoViewProgress = (ProgressBar) view.findViewById(R.id.video_view_progress);
mProgressBar = (ProgressWheel) view.findViewById(R.id.load_progress);
}
@Override
public void onVideoLoadingComplete(String uri, VideoLoadingListener listener, File file) {
mVideoView.setVideoURI(Uri.fromFile(file));
@ -600,21 +635,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
invalidateOptionsMenu();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
mVideoLoader = TwidereApplication.getInstance(getActivity()).getVideoLoader();
mVideoProgressRunnable = new VideoPlayProgressRunnable(mVideoViewProgress.getHandler(),
mVideoViewProgress, mVideoView);
mVideoView.setOnPreparedListener(this);
mVideoView.setOnErrorListener(this);
mVideoView.setOnCompletionListener(this);
loadVideo();
}
@Override
public void onVideoLoadingFailed(String uri, VideoLoadingListener listener, Exception e) {
mProgressBar.setVisibility(View.GONE);
@ -633,11 +653,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
mProgressBar.setProgress(current / (float) total);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_media_page_video, container, false);
}
@Override
public void onVideoLoadingStarted(String uri, VideoLoadingListener listener) {
mProgressBar.setVisibility(View.VISIBLE);
@ -645,6 +660,29 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
invalidateOptionsMenu();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (!isVisibleToUser && mVideoView != null && mVideoView.isPlaying()) {
mVideoView.pause();
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
mVideoLoader = TwidereApplication.getInstance(getActivity()).getVideoLoader();
mVideoProgressRunnable = new VideoPlayProgressRunnable(mVideoViewProgress.getHandler(),
mVideoViewProgress, mVideoView);
mVideoView.setOnPreparedListener(this);
mVideoView.setOnErrorListener(this);
mVideoView.setOnCompletionListener(this);
loadVideo();
}
private Pair<String, String> getBestVideoUrlAndType(ParcelableMedia media) {
if (media == null) return null;
switch (media.type) {
@ -688,6 +726,36 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
mSaveFileTask = AsyncTaskUtils.executeTask(new SaveFileTask(getActivity(), file, mimeType, saveDir));
}
private static class VideoPlayProgressRunnable implements Runnable {
private final Handler mHandler;
private final ProgressBar mProgressBar;
private final MediaController.MediaPlayerControl mMediaPlayerControl;
VideoPlayProgressRunnable(Handler handler, ProgressBar progressBar,
MediaController.MediaPlayerControl mediaPlayerControl) {
mHandler = handler;
mProgressBar = progressBar;
mMediaPlayerControl = mediaPlayerControl;
mProgressBar.setMax(1000);
}
@Override
public void run() {
final int duration = mMediaPlayerControl.getDuration();
final int position = mMediaPlayerControl.getCurrentPosition();
if (duration <= 0 || position < 0) return;
mProgressBar.setProgress(Math.round(1000 * position / (float) duration));
mHandler.postDelayed(this, 16);
}
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_media_page_video, container, false);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@ -714,30 +782,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
shareProvider.setShareIntent(intent);
}
private static class VideoPlayProgressRunnable implements Runnable {
private final Handler mHandler;
private final ProgressBar mProgressBar;
private final MediaController.MediaPlayerControl mMediaPlayerControl;
VideoPlayProgressRunnable(Handler handler, ProgressBar progressBar,
MediaController.MediaPlayerControl mediaPlayerControl) {
mHandler = handler;
mProgressBar = progressBar;
mMediaPlayerControl = mediaPlayerControl;
mProgressBar.setMax(1000);
}
@Override
public void run() {
final int duration = mMediaPlayerControl.getDuration();
final int position = mMediaPlayerControl.getCurrentPosition();
if (duration <= 0 || position < 0) return;
mProgressBar.setProgress(Math.round(1000 * position / (float) duration));
mHandler.postDelayed(this, 16);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
@ -776,14 +820,6 @@ public final class MediaViewerActivity extends ThemedAppCompatActivity implement
super.onPause();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (!isVisibleToUser && mVideoView != null && mVideoView.isPlaying()) {
mVideoView.pause();
}
}
}
}

View File

@ -76,15 +76,16 @@ public class RecyclerViewNavigationHelper implements KeyboardShortcutCallback {
final int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
final int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
final int itemCount = adapter.getItemCount();
final boolean backupOutsideRange = positionBackup > lastVisibleItemPosition || positionBackup < firstVisibleItemPosition;
if (focusedChild != null) {
position = view.getChildLayoutPosition(focusedChild);
} else if (firstVisibleItemPosition == 0) {
position = -1;
} else if (lastVisibleItemPosition == itemCount - 1) {
position = itemCount;
} else if (direction > 0 && positionBackup < firstVisibleItemPosition) {
} else if (direction > 0 && backupOutsideRange) {
position = firstVisibleItemPosition;
} else if (direction < 0 && positionBackup > lastVisibleItemPosition) {
} else if (direction < 0 && backupOutsideRange) {
position = lastVisibleItemPosition;
} else {
position = positionBackup;

View File

@ -19,10 +19,8 @@
package org.mariotaku.twidere.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.widget.ImageView;
@ -43,11 +41,6 @@ public class BoundsImageView extends ImageView {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public BoundsImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
setFrame(getLeft(), getTop(), getRight(), getBottom());

View File

@ -670,6 +670,8 @@
<string name="members">Members</string>
<string name="subscribers">Subscriber</string>
<string name="read_from_bottom">Read from bottom</string>
<string name="read_from_bottom_summary_off">Jump to latest tweet after refreshing</string>
<string name="read_from_bottom_summary_on">Keep read position after refreshing</string>
<string name="register">Register</string>
<string name="follows">Follows</string>
<string name="belongs_to">Belongs to</string>

View File

@ -79,6 +79,8 @@
<CheckBoxPreference
android:defaultValue="false"
android:key="read_from_bottom"
android:summaryOff="@string/read_from_bottom_summary_off"
android:summaryOn="@string/read_from_bottom_summary_on"
android:title="@string/read_from_bottom"/>
<CheckBoxPreference
android:defaultValue="false"