added load progress for video

improved keyboard shortcut
This commit is contained in:
Mariotaku Lee 2015-04-21 15:11:14 +08:00
parent 46aea213ee
commit 5d7765b6d3
22 changed files with 634 additions and 308 deletions

View File

@ -11,4 +11,5 @@ include ':twidere.donate.nyanwp.wear'
include ':twidere.component.nyan'
include ':twidere.extension.streaming'
include ':twidere.extension.twitlonger'
include ':twidere.extension.push.xiaomi'
include ':twidere.extension.push.xiaomi'
include ':twidere.extension.launcher.compose'

View File

@ -29,12 +29,12 @@ import org.mariotaku.twidere.constant.SharedPreferenceConstants;
*/
public interface TwidereConstants extends SharedPreferenceConstants, IntentConstants {
public static final String APP_NAME = "Twidere";
public static final String APP_PACKAGE_NAME = "org.mariotaku.twidere";
public static final String APP_PROJECT_URL = "https://github.com/mariotaku/twidere";
public static final String APP_PROJECT_EMAIL = "twidere.project@gmail.com";
public static final String TWIDERE_APP_NAME = "Twidere";
public static final String TWIDERE_PACKAGE_NAME = "org.mariotaku.twidere";
public static final String TWIDERE_PROJECT_URL = "https://github.com/mariotaku/twidere";
public static final String TWIDERE_PROJECT_EMAIL = "twidere.project@gmail.com";
public static final String LOGTAG = APP_NAME;
public static final String LOGTAG = TWIDERE_APP_NAME;
public static final String USER_NICKNAME_PREFERENCES_NAME = "user_nicknames";
public static final String USER_COLOR_PREFERENCES_NAME = "user_colors";

View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,42 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
apply plugin: 'com.android.application'
apply from: rootProject.file('global.gradle')
android {
defaultConfig {
applicationId "org.mariotaku.twidere.extension.launcher.compose"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile project(':twidere.library.extension')
compile fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/mariotaku/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,32 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension.launcher.compose;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,38 @@
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest package="org.mariotaku.twidere.extension.launcher.compose"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoDisplay">
<activity android:name=".MainActivity"
android:clearTaskOnLaunch="true"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,47 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension.launcher.compose;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import org.mariotaku.twidere.TwidereConstants;
/**
* Created by mariotaku on 15/4/21.
*/
public class MainActivity extends Activity implements TwidereConstants {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = new Intent(INTENT_ACTION_COMPOSE);
intent.setPackage(TWIDERE_PACKAGE_NAME);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
try {
startActivity(intent);
} catch (ActivityNotFoundException ignored) {
}
finish();
}
}

View File

@ -0,0 +1,22 @@
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Twidere Compose Launcher</string>
</resources>

View File

@ -32,7 +32,7 @@ import org.mariotaku.twidere.activity.iface.IControlBarActivity;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.ShortcutCallback;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.view.iface.IExtendedView.OnFitSystemWindowsListener;
@ -41,7 +41,7 @@ import java.util.ArrayList;
@SuppressLint("Registered")
public class BaseActionBarActivity extends ThemedActionBarActivity implements Constants,
OnFitSystemWindowsListener, SystemWindowsInsetsCallback, IControlBarActivity,
ShortcutCallback {
KeyboardShortcutCallback {
private boolean mInstanceStateSaved;
private boolean mIsVisible;

View File

@ -91,7 +91,7 @@ import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ColorUtils;
import org.mariotaku.twidere.util.CustomTabUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.ShortcutCallback;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.MathUtils;
import org.mariotaku.twidere.util.MultiSelectEventHandler;
import org.mariotaku.twidere.util.ParseUtils;
@ -287,6 +287,7 @@ public class HomeActivity extends BaseActionBarActivity implements OnClickListen
mSlidingMenu.showContent(true);
} else {
mSlidingMenu.showMenu(true);
setControlBarVisibleAnimate(true);
}
return true;
}
@ -684,16 +685,16 @@ public class HomeActivity extends BaseActionBarActivity implements OnClickListen
private boolean handleFragmentKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event) {
final Fragment fragment = getCurrentVisibleFragment();
if (fragment instanceof ShortcutCallback) {
return ((ShortcutCallback) fragment).handleKeyboardShortcutSingle(keyCode, event);
if (fragment instanceof KeyboardShortcutCallback) {
return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutSingle(keyCode, event);
}
return false;
}
private boolean handleFragmentKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
final Fragment fragment = getCurrentVisibleFragment();
if (fragment instanceof ShortcutCallback) {
return ((ShortcutCallback) fragment).handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
if (fragment instanceof KeyboardShortcutCallback) {
return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
}
return false;
}

View File

@ -50,7 +50,7 @@ import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.fragment.support.SearchFragment;
import org.mariotaku.twidere.fragment.support.UserFragment;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.ShortcutCallback;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.MultiSelectEventHandler;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
@ -221,16 +221,16 @@ public class LinkHandlerActivity extends BaseActionBarActivity implements System
private boolean handleFragmentKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
final Fragment fragment = getCurrentVisibleFragment();
if (fragment instanceof ShortcutCallback) {
return ((ShortcutCallback) fragment).handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
if (fragment instanceof KeyboardShortcutCallback) {
return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
}
return false;
}
private boolean handleFragmentKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event) {
final Fragment fragment = getCurrentVisibleFragment();
if (fragment instanceof ShortcutCallback) {
return ((ShortcutCallback) fragment).handleKeyboardShortcutSingle(keyCode, event);
if (fragment instanceof KeyboardShortcutCallback) {
return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutSingle(keyCode, event);
}
return false;
}

View File

@ -228,7 +228,7 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
super.onBaseViewCreated(view, savedInstanceState);
mImageView = (SubsamplingScaleImageView) view.findViewById(R.id.image_view);
mGifImageView = (GifTextureView) view.findViewById(R.id.gif_image_view);
mProgressBar = (ProgressWheel) view.findViewById(R.id.progress);
mProgressBar = (ProgressWheel) view.findViewById(R.id.load_progress);
}
@Override
@ -324,6 +324,11 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
activity.setBarVisibility(false);
}
public void onZoomOut() {
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
activity.setBarVisibility(true);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@ -349,21 +354,11 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
shareProvider.setShareIntent(intent);
}
public void onZoomOut() {
final MediaViewerActivity activity = (MediaViewerActivity) getActivity();
activity.setBarVisibility(true);
}
private ParcelableMedia getMedia() {
final Bundle args = getArguments();
return args.getParcelable(EXTRA_MEDIA);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_image_page, menu);
}
private void loadImage() {
getLoaderManager().destroyLoader(0);
if (!mLoaderInitialized) {
@ -387,22 +382,8 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_OPEN_IN_BROWSER: {
openInBrowser();
return true;
}
case MENU_SAVE: {
saveToGallery();
return true;
}
case MENU_REFRESH: {
loadImage();
return true;
}
}
return super.onOptionsItemSelected(item);
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_image_page, menu);
}
private void saveToGallery() {
@ -427,6 +408,25 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
// mImageView.resetScale();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_OPEN_IN_BROWSER: {
openInBrowser();
return true;
}
case MENU_SAVE: {
saveToGallery();
return true;
}
case MENU_REFRESH: {
loadImage();
return true;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
@ -526,6 +526,7 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
private SaveFileTask mSaveFileTask;
private File mVideoFile;
private Pair<String, String> mVideoUrlAndType;
private ProgressWheel mProgressBar;
public boolean isLoopEnabled() {
return getArguments().getBoolean(EXTRA_LOOP, false);
@ -544,13 +545,6 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
// mVideoViewProgress.setVisibility(View.GONE);
}
@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);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mVideoViewProgress.removeCallbacks(mVideoProgressRunnable);
@ -558,6 +552,14 @@ public final class MediaViewerActivity extends ThemedActionBarActivity 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()) {
@ -574,6 +576,22 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
}
@Override
public void onVideoLoadingCancelled(String uri, VideoLoadingListener listener) {
mProgressBar.setVisibility(View.GONE);
mProgressBar.setProgress(0);
invalidateOptionsMenu();
}
@Override
public void onVideoLoadingComplete(String uri, VideoLoadingListener listener, File file) {
mVideoView.setVideoURI(Uri.fromFile(file));
mVideoFile = file;
mProgressBar.setVisibility(View.GONE);
mProgressBar.setProgress(0);
invalidateOptionsMenu();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -590,60 +608,32 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
@Override
public void onVideoLoadingCancelled(String uri, VideoLoadingListener listener) {
public void onVideoLoadingFailed(String uri, VideoLoadingListener listener, Exception e) {
mProgressBar.setVisibility(View.GONE);
mProgressBar.setProgress(0);
invalidateOptionsMenu();
}
@Override
public void onVideoLoadingProgressUpdate(String uri, VideoLoadingListener listener, int current, int total) {
if (total <= 0) {
if (!mProgressBar.isSpinning()) {
mProgressBar.spin();
}
return;
}
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 onVideoLoadingComplete(String uri, VideoLoadingListener listener, File file) {
mVideoView.setVideoURI(Uri.fromFile(file));
mVideoFile = file;
invalidateOptionsMenu();
}
@Override
public void onVideoLoadingFailed(String uri, VideoLoadingListener listener, Exception e) {
invalidateOptionsMenu();
}
@Override
public void onVideoLoadingProgressUpdate(String uri, VideoLoadingListener listener, int current, int total) {
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final File file = mVideoFile;
final Pair<String, String> linkAndType = mVideoUrlAndType;
final boolean isLoading = getLoaderManager().hasRunningLoaders();
final boolean hasVideo = file != null && file.exists() && linkAndType != null;
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasVideo && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasVideo && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasVideo && !isLoading);
if (!hasVideo) 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, linkAndType.second);
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 onVideoLoadingStarted(String uri, VideoLoadingListener listener) {
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.spin();
invalidateOptionsMenu();
}
@ -670,11 +660,6 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_video_page, menu);
}
private ParcelableMedia getMedia() {
final Bundle args = getArguments();
return args.getParcelable(EXTRA_MEDIA);
@ -696,18 +681,29 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_SAVE: {
saveToGallery();
return true;
}
case MENU_REFRESH: {
loadVideo();
return true;
}
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final File file = mVideoFile;
final Pair<String, String> linkAndType = mVideoUrlAndType;
final boolean isLoading = linkAndType != null && mVideoLoader.isLoading(linkAndType.first);
final boolean hasVideo = file != null && file.exists() && linkAndType != null;
MenuUtils.setMenuItemAvailability(menu, R.id.refresh, !hasVideo && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.share, hasVideo && !isLoading);
MenuUtils.setMenuItemAvailability(menu, R.id.save, hasVideo && !isLoading);
if (!hasVideo) 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, linkAndType.second);
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));
}
return super.onOptionsItemSelected(item);
shareProvider.setShareIntent(intent);
}
private static class VideoPlayProgressRunnable implements Runnable {
@ -735,6 +731,28 @@ public final class MediaViewerActivity extends ThemedActionBarActivity implement
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_media_viewer_video_page, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_SAVE: {
saveToGallery();
return true;
}
case MENU_REFRESH: {
loadVideo();
return true;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void onResume() {
super.onResume();

View File

@ -32,8 +32,9 @@ import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.ShortcutCallback;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
import org.mariotaku.twidere.util.RecyclerViewUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
@ -49,7 +50,7 @@ import static org.mariotaku.twidere.util.Utils.setMenuForStatus;
* Created by mariotaku on 14/11/5.
*/
public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<AbsStatusesAdapter<Data>>
implements LoaderCallbacks<Data>, StatusAdapterListener, ShortcutCallback {
implements LoaderCallbacks<Data>, StatusAdapterListener, KeyboardShortcutCallback {
private final Object mStatusesBusCallback;
private SharedPreferences mPreferences;
@ -71,7 +72,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<A
getFragmentManager(), getTwitterWrapper(), status, item);
}
};
private int mPositionBackup;
private RecyclerViewNavigationHelper mRecyclerViewNavigationHelper;
protected AbsStatusesFragment() {
mStatusesBusCallback = createMessageBusCallback();
@ -133,39 +134,9 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<A
}
@Override
public boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
if (!KeyboardShortcutsHandler.isValidForHotkey(keyCode, event)) return false;
String action = mKeyboardShortcutsHandler.getKeyAction("navigation", keyCode, event);
final LinearLayoutManager layoutManager = getLayoutManager();
final RecyclerView recyclerView = getRecyclerView();
final View focusedChild = RecyclerViewUtils.findRecyclerViewChild(recyclerView, layoutManager.getFocusedChild());
final int position;
if (focusedChild != null) {
position = recyclerView.getChildLayoutPosition(focusedChild);
} else if (layoutManager.findFirstVisibleItemPosition() == 0) {
position = -1;
} else {
final int itemCount = getAdapter().getItemCount();
if (layoutManager.findLastVisibleItemPosition() == itemCount - 1) {
position = itemCount;
} else {
position = mPositionBackup;
}
}
mPositionBackup = position;
if (action != null) {
switch (action) {
case "navigation.previous": {
RecyclerViewUtils.focusNavigate(recyclerView, layoutManager, position, -1);
return true;
}
case "navigation.next": {
RecyclerViewUtils.focusNavigate(recyclerView, layoutManager, position, 1);
return true;
}
}
}
return false;
public boolean handleKeyboardShortcutRepeat(final int keyCode, final int repeatCount,
@NonNull final KeyEvent event) {
return mRecyclerViewNavigationHelper.handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
}
@Override
@ -374,7 +345,10 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<A
final FragmentActivity activity = getActivity();
final TwidereApplication application = TwidereApplication.getInstance(activity);
mKeyboardShortcutsHandler = application.getKeyboardShortcutsHandler();
getAdapter().setListener(this);
final AbsStatusesAdapter<Data> adapter = getAdapter();
final RecyclerView recyclerView = getRecyclerView();
final LinearLayoutManager layoutManager = getLayoutManager();
adapter.setListener(this);
getScrollListener().setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
@ -383,6 +357,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<A
}
}
});
mRecyclerViewNavigationHelper = new RecyclerViewNavigationHelper(mKeyboardShortcutsHandler, recyclerView, layoutManager, adapter);
final Bundle loaderArgs = new Bundle(getArguments());
loaderArgs.putBoolean(EXTRA_FROM_USER, true);

View File

@ -21,32 +21,61 @@ package org.mariotaku.twidere.fragment.support;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.View;
import org.mariotaku.twidere.adapter.AbsUsersAdapter;
import org.mariotaku.twidere.adapter.AbsUsersAdapter.UserAdapterListener;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.UserViewHolder;
abstract class AbsUsersFragment<Data> extends AbsContentListFragment<AbsUsersAdapter<Data>> implements LoaderCallbacks<Data>, UserAdapterListener {
abstract class AbsUsersFragment<Data> extends AbsContentListFragment<AbsUsersAdapter<Data>>
implements LoaderCallbacks<Data>, UserAdapterListener, KeyboardShortcutCallback {
private KeyboardShortcutsHandler mKeyboardShortcutsHandler;
private RecyclerViewNavigationHelper mRecyclerViewNavigationHelper;
public final Data getData() {
return getAdapter().getData();
}
@Override
public boolean handleKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event) {
return false;
}
@Override
public boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
return mRecyclerViewNavigationHelper.handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getAdapter().setListener(this);
final FragmentActivity activity = getActivity();
final TwidereApplication application = TwidereApplication.getInstance(activity);
mKeyboardShortcutsHandler = application.getKeyboardShortcutsHandler();
final AbsUsersAdapter<Data> adapter = getAdapter();
final RecyclerView recyclerView = getRecyclerView();
final LinearLayoutManager layoutManager = getLayoutManager();
adapter.setListener(this);
mRecyclerViewNavigationHelper = new RecyclerViewNavigationHelper(mKeyboardShortcutsHandler, recyclerView, layoutManager, adapter);
final Bundle loaderArgs = new Bundle(getArguments());
loaderArgs.putBoolean(EXTRA_FROM_USER, true);
getLoaderManager().initLoader(0, loaderArgs, this);

View File

@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
@ -40,6 +41,7 @@ import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.FixedLinearLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@ -70,7 +72,10 @@ import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ContentListScrollListener;
import org.mariotaku.twidere.util.ContentListScrollListener.ContentListSupport;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.MultiSelectManager;
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver;
@ -85,7 +90,7 @@ import static org.mariotaku.twidere.util.Utils.openMessageConversation;
public class DirectMessagesFragment extends BaseSupportFragment implements LoaderCallbacks<Cursor>,
RefreshScrollTopInterface, OnRefreshListener, MessageEntriesAdapterListener,
ControlBarOffsetListener, ContentListSupport {
ControlBarOffsetListener, ContentListSupport, KeyboardShortcutCallback {
private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver(
this, 0, this);
@ -100,11 +105,54 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
private SwipeRefreshLayout mSwipeRefreshLayout;
private View mProgressContainer;
private LinearLayoutManager mLayoutManager;
private KeyboardShortcutsHandler mKeyboardShortcutsHandler;
private RecyclerViewNavigationHelper mRecyclerViewNavigationHelper;
private Rect mSystemWindowsInsets = new Rect();
private int mControlBarOffsetPixels;
@Override
public MessageEntriesAdapter getAdapter() {
return mAdapter;
}
@Override
public boolean isRefreshing() {
if (mSwipeRefreshLayout == null || mAdapter == null) return false;
return mSwipeRefreshLayout.isRefreshing() || mAdapter.isLoadMoreIndicatorVisible();
}
public void setRefreshing(boolean refreshing) {
if (mAdapter == null || refreshing == mSwipeRefreshLayout.isRefreshing()) return;
mSwipeRefreshLayout.setRefreshing(refreshing && !mAdapter.isLoadMoreIndicatorVisible());
}
@Override
public void onLoadMoreContents() {
loadMoreMessages();
}
@Override
public void setControlVisible(boolean visible) {
final FragmentActivity activity = getActivity();
if (activity instanceof BaseActionBarActivity) {
((BaseActionBarActivity) activity).setControlBarVisibleAnimate(visible);
}
}
public final LongSparseArray<Set<Long>> getUnreadCountsToRemove() {
return mUnreadCountsToRemove;
}
@Override
public boolean handleKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event) {
return false;
}
@Override
public boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
return mRecyclerViewNavigationHelper.handleKeyboardShortcutRepeat(keyCode, repeatCount, event);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@ -113,9 +161,98 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
}
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_view, container, false);
}
private Rect mSystemWindowsInsets = new Rect();
private int mControlBarOffsetPixels;
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final View view = getView();
if (view == null) throw new AssertionError();
final TwidereApplication application = TwidereApplication.getInstance(getActivity());
mKeyboardShortcutsHandler = application.getKeyboardShortcutsHandler();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final Context viewContext = view.getContext();
mMultiSelectManager = getMultiSelectManager();
mAdapter = new MessageEntriesAdapter(viewContext);
mAdapter.setListener(this);
mLayoutManager = new FixedLinearLayoutManager(viewContext);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeColors(ThemeUtils.getUserAccentColor(viewContext));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
mRecyclerViewNavigationHelper = new RecyclerViewNavigationHelper(mKeyboardShortcutsHandler, mRecyclerView, mLayoutManager, mAdapter);
final ContentListScrollListener scrollListener = new ContentListScrollListener(this);
scrollListener.setTouchSlop(ViewConfiguration.get(viewContext).getScaledTouchSlop());
mRecyclerView.setOnScrollListener(scrollListener);
final DividerItemDecoration itemDecoration = new DividerItemDecoration(viewContext, mLayoutManager.getOrientation());
final Resources res = viewContext.getResources();
final int decorPaddingLeft = res.getDimensionPixelSize(R.dimen.element_spacing_normal) * 3
+ res.getDimensionPixelSize(R.dimen.icon_size_status_profile_image);
itemDecoration.setPadding(decorPaddingLeft, 0, 0, 0);
itemDecoration.setDecorationEndOffset(1);
mRecyclerView.addItemDecoration(itemDecoration);
getLoaderManager().initLoader(0, null, this);
setListShown(false);
}
@Override
public void onStart() {
super.onStart();
final ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(Accounts.CONTENT_URI, true, mReloadContentObserver);
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.register(this);
mAdapter.updateReadState();
updateRefreshState();
}
@Override
public void onStop() {
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.unregister(this);
final ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mReloadContentObserver);
super.onStop();
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case MENU_COMPOSE: {
openMessageConversation(getActivity(), -1, -1);
break;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mProgressContainer = view.findViewById(R.id.progress_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
}
@Override
public void onDetach() {
final FragmentActivity activity = getActivity();
if (activity instanceof IControlBarActivity) {
((IControlBarActivity) activity).unregisterControlBarOffsetListener(this);
}
super.onDetach();
}
@Override
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
}
@Override
protected void fitSystemWindows(Rect insets) {
@ -131,54 +268,6 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
updateRefreshProgressOffset();
}
private void updateRefreshProgressOffset() {
if (mSystemWindowsInsets.top == 0 || mSwipeRefreshLayout == null || isRefreshing()) return;
final float density = getResources().getDisplayMetrics().density;
final int progressCircleDiameter = mSwipeRefreshLayout.getProgressCircleDiameter();
final int swipeStart = (mSystemWindowsInsets.top - mControlBarOffsetPixels) - progressCircleDiameter;
// 64: SwipeRefreshLayout.DEFAULT_CIRCLE_TARGET
final int swipeDistance = Math.round(64 * density);
mSwipeRefreshLayout.setProgressViewOffset(true, swipeStart, swipeStart + swipeDistance);
}
@Override
public void onDetach() {
final FragmentActivity activity = getActivity();
if (activity instanceof IControlBarActivity) {
((IControlBarActivity) activity).unregisterControlBarOffsetListener(this);
}
super.onDetach();
}
@Override
public void onEntryClick(int position, DirectMessageEntry entry) {
Utils.openMessageConversation(getActivity(), entry.account_id, entry.conversation_id);
}
@Override
public void onUserClick(int position, DirectMessageEntry entry) {
Utils.openUserProfile(getActivity(), entry.account_id, entry.conversation_id, entry.screen_name, null);
}
@Override
public void onRefresh() {
triggerRefresh();
}
private void setListShown(boolean shown) {
mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE);
mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE);
}
@Override
public void onBaseViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onBaseViewCreated(view, savedInstanceState);
mProgressContainer = view.findViewById(R.id.progress_container);
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
}
@Override
public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
final Uri uri = DirectMessages.ConversationEntries.CONTENT_URI;
@ -211,54 +300,15 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_view, container, false);
public void onEntryClick(int position, DirectMessageEntry entry) {
Utils.openMessageConversation(getActivity(), entry.account_id, entry.conversation_id);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final View view = getView();
if (view == null) throw new AssertionError();
mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final Context viewContext = view.getContext();
mMultiSelectManager = getMultiSelectManager();
mAdapter = new MessageEntriesAdapter(viewContext);
mAdapter.setListener(this);
mLayoutManager = new FixedLinearLayoutManager(viewContext);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeColors(ThemeUtils.getUserAccentColor(viewContext));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
final ContentListScrollListener scrollListener = new ContentListScrollListener(this);
scrollListener.setTouchSlop(ViewConfiguration.get(viewContext).getScaledTouchSlop());
mRecyclerView.setOnScrollListener(scrollListener);
final DividerItemDecoration itemDecoration = new DividerItemDecoration(viewContext, mLayoutManager.getOrientation());
final Resources res = viewContext.getResources();
final int decorPaddingLeft = res.getDimensionPixelSize(R.dimen.element_spacing_normal) * 3
+ res.getDimensionPixelSize(R.dimen.icon_size_status_profile_image);
itemDecoration.setPadding(decorPaddingLeft, 0, 0, 0);
itemDecoration.setDecorationEndOffset(1);
mRecyclerView.addItemDecoration(itemDecoration);
getLoaderManager().initLoader(0, null, this);
setListShown(false);
public void onUserClick(int position, DirectMessageEntry entry) {
Utils.openUserProfile(getActivity(), entry.account_id, entry.conversation_id, entry.screen_name, null);
}
@Override
public void onStart() {
super.onStart();
final ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(Accounts.CONTENT_URI, true, mReloadContentObserver);
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.register(this);
mAdapter.updateReadState();
updateRefreshState();
}
@Subscribe
public void onGetMessagesTaskChanged(GetMessagesTaskEvent event) {
if (event.uri.equals(Inbox.CONTENT_URI) && !event.running) {
@ -269,23 +319,8 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
}
@Override
public void onStop() {
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.unregister(this);
final ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mReloadContentObserver);
super.onStop();
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case MENU_COMPOSE: {
openMessageConversation(getActivity(), -1, -1);
break;
}
}
return super.onOptionsItemSelected(item);
public void onRefresh() {
triggerRefresh();
}
@Override
@ -323,12 +358,6 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
return true;
}
@Override
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
}
protected long getAccountId() {
final Bundle args = getArguments();
return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1;
@ -339,17 +368,6 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
setRefreshing(twitter != null && (twitter.isReceivedDirectMessagesRefreshing() || twitter.isSentDirectMessagesRefreshing()));
}
public void setRefreshing(boolean refreshing) {
if (mAdapter == null || refreshing == mSwipeRefreshLayout.isRefreshing()) return;
mSwipeRefreshLayout.setRefreshing(refreshing && !mAdapter.isLoadMoreIndicatorVisible());
}
@Override
public boolean isRefreshing() {
if (mSwipeRefreshLayout == null || mAdapter == null) return false;
return mSwipeRefreshLayout.isRefreshing() || mAdapter.isLoadMoreIndicatorVisible();
}
private void addReadPosition(final int firstVisibleItem) {
if (mFirstVisibleItem != firstVisibleItem) {
mReadPositions.add(firstVisibleItem);
@ -395,24 +413,6 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
});
}
@Override
public MessageEntriesAdapter getAdapter() {
return mAdapter;
}
@Override
public void setControlVisible(boolean visible) {
final FragmentActivity activity = getActivity();
if (activity instanceof BaseActionBarActivity) {
((BaseActionBarActivity) activity).setControlBarVisibleAnimate(visible);
}
}
@Override
public void onLoadMoreContents() {
loadMoreMessages();
}
private void removeUnreadCounts() {
if (mRemoveUnreadCountsTask != null && mRemoveUnreadCountsTask.getStatus() == AsyncTask.Status.RUNNING)
return;
@ -420,6 +420,21 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
AsyncTaskUtils.executeTask(mRemoveUnreadCountsTask);
}
private void setListShown(boolean shown) {
mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE);
mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE);
}
private void updateRefreshProgressOffset() {
if (mSystemWindowsInsets.top == 0 || mSwipeRefreshLayout == null || isRefreshing()) return;
final float density = getResources().getDisplayMetrics().density;
final int progressCircleDiameter = mSwipeRefreshLayout.getProgressCircleDiameter();
final int swipeStart = (mSystemWindowsInsets.top - mControlBarOffsetPixels) - progressCircleDiameter;
// 64: SwipeRefreshLayout.DEFAULT_CIRCLE_TARGET
final int swipeDistance = Math.round(64 * density);
mSwipeRefreshLayout.setProgressViewOffset(true, swipeStart, swipeStart + swipeDistance);
}
static class RemoveUnreadCountsTask extends AsyncTask<Object, Object, Object> {
private final Set<Integer> read_positions;
private final MessageEntriesAdapter adapter;

View File

@ -249,7 +249,7 @@ public class KeyboardShortcutsHandler implements Constants {
editor.apply();
}
public static interface ShortcutCallback {
public static interface KeyboardShortcutCallback {
boolean handleKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event);
boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event);

View File

@ -0,0 +1,80 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.KeyEvent;
import android.view.View;
/**
* Created by mariotaku on 15/4/21.
*/
public class RecyclerViewNavigationHelper {
private int positionBackup;
private final KeyboardShortcutsHandler handler;
private final RecyclerView view;
private final LinearLayoutManager manager;
private final Adapter<ViewHolder> adapter;
public RecyclerViewNavigationHelper(KeyboardShortcutsHandler handler, RecyclerView view,
LinearLayoutManager manager, Adapter<ViewHolder> adapter) {
this.handler = handler;
this.view = view;
this.manager = manager;
this.adapter = adapter;
}
public boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
final String action = handler.getKeyAction("navigation", keyCode, event);
if (action == null) return false;
final LinearLayoutManager layoutManager = this.manager;
final View focusedChild = RecyclerViewUtils.findRecyclerViewChild(view, layoutManager.getFocusedChild());
final int position;
if (focusedChild != null) {
position = view.getChildLayoutPosition(focusedChild);
} else if (layoutManager.findFirstVisibleItemPosition() == 0) {
position = -1;
} else {
final int itemCount = adapter.getItemCount();
if (layoutManager.findLastVisibleItemPosition() == itemCount - 1) {
position = itemCount;
} else {
position = positionBackup;
}
}
positionBackup = position;
switch (action) {
case "navigation.previous": {
RecyclerViewUtils.focusNavigate(view, layoutManager, position, -1);
return true;
}
case "navigation.next": {
RecyclerViewUtils.focusNavigate(view, layoutManager, position, 1);
return true;
}
}
return false;
}
}

View File

@ -3576,9 +3576,9 @@ public final class Utils implements Constants, TwitterConstants {
final PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
final String version_name = pi.versionName;
cb.setClientVersion(pi.versionName);
cb.setClientName(APP_NAME);
cb.setClientURL(APP_PROJECT_URL);
cb.setHttpUserAgent(APP_NAME + " " + APP_PROJECT_URL + " / " + version_name
cb.setClientName(TWIDERE_APP_NAME);
cb.setClientURL(TWIDERE_PROJECT_URL);
cb.setHttpUserAgent(TWIDERE_APP_NAME + " " + TWIDERE_PROJECT_URL + " / " + version_name
+ (gzipCompressing ? " (gzip)" : ""));
} catch (final PackageManager.NameNotFoundException e) {
throw new AssertionError(e);

View File

@ -69,6 +69,10 @@ public class VideoLoader {
return null;
}
public boolean isLoading(String url) {
return mTaskManager.hasRunningTasksForTag(url);
}
public int loadVideo(String uri, VideoLoadingListener listener) {
if (mTaskManager.hasRunningTasksForTag(uri)) {

View File

@ -21,7 +21,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<pl.droidsonroids.gif.GifTextureView
android:id="@+id/gif_image_view"
@ -35,10 +36,11 @@
android:layout_height="match_parent"/>
<com.pnikosis.materialishprogress.ProgressWheel
android:id="@+id/progress"
android:id="@+id/load_progress"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_gravity="center"
android:visibility="gone"
app:matProg_barColor="@color/branding_color"/>

View File

@ -20,8 +20,10 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<RelativeLayout
android:layout_width="match_parent"
@ -49,12 +51,12 @@
</RelativeLayout>
<ProgressBar
android:id="@+id/progress"
<com.pnikosis.materialishprogress.ProgressWheel
android:id="@+id/load_progress"
android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal"
android:layout_gravity="center"
android:visibility="gone"/>
android:visibility="gone"
app:matProg_barColor="@color/branding_color"/>
</FrameLayout>