カラム設定の高さ調整。カラム設定にストリーミングOFFと画面復帰時の新着取得OFFを追加。

This commit is contained in:
tateisu 2017-05-11 23:07:29 +09:00
parent ed37e9cf35
commit c13b92d7f8
28 changed files with 413 additions and 239 deletions

View File

@ -56,10 +56,11 @@ import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.table.MutedApp;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.table.UserRelation;
import jp.juggler.subwaytooter.util.ActionsDialog;
import jp.juggler.subwaytooter.dialog.ActionsDialog;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LinkClickContext;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.MyClickableSpan;
import jp.juggler.subwaytooter.util.Utils;
import okhttp3.Request;
import okhttp3.RequestBody;
@ -125,7 +126,8 @@ public class ActMain extends AppCompatActivity
bResume = true;
log.d( "onResume" );
super.onResume();
HTMLDecoder.link_callback = link_click_listener;
MyClickableSpan.link_callback = link_click_listener;
// アカウント設定から戻ってきたらカラムを消す必要があるかもしれない
{
@ -209,7 +211,7 @@ public class ActMain extends AppCompatActivity
app_state.stream_reader.onPause();
HTMLDecoder.link_callback = null;
MyClickableSpan.link_callback = null;
super.onPause();
}
@ -1434,7 +1436,7 @@ public class ActMain extends AppCompatActivity
}
final HTMLDecoder.LinkClickCallback link_click_listener = new HTMLDecoder.LinkClickCallback() {
final MyClickableSpan.LinkClickCallback link_click_listener = new MyClickableSpan.LinkClickCallback() {
@Override public void onClickLink( LinkClickContext lcc, String url ){
openChromeTab( (SavedAccount) lcc, url, false );
}

View File

@ -60,11 +60,11 @@ import jp.juggler.subwaytooter.dialog.DlgConfirm;
import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.table.AcctSet;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.ActionsDialog;
import jp.juggler.subwaytooter.dialog.ActionsDialog;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.MyEditText;
import jp.juggler.subwaytooter.util.MyNetworkImageView;
import jp.juggler.subwaytooter.view.MyEditText;
import jp.juggler.subwaytooter.view.MyNetworkImageView;
import jp.juggler.subwaytooter.util.PostAttachment;
import jp.juggler.subwaytooter.util.Utils;
import okhttp3.MediaType;

View File

@ -9,8 +9,6 @@ import android.text.TextUtils;
import android.view.View;
import android.widget.ListView;
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection;
import org.json.JSONException;
import org.json.JSONObject;
@ -41,13 +39,13 @@ import jp.juggler.subwaytooter.table.MutedWord;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.table.UserRelation;
import jp.juggler.subwaytooter.util.BucketList;
import jp.juggler.subwaytooter.util.DuplicateMap;
import jp.juggler.subwaytooter.api.DuplicateMap;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.view.MyListView;
import jp.juggler.subwaytooter.util.ScrollPosition;
import jp.juggler.subwaytooter.util.Utils;
class Column {
class Column implements StreamReader.Callback {
private static final LogCategory log = new LogCategory( "Column" );
@ -59,12 +57,12 @@ class Column {
private boolean isResume(){
if( callback_ref == null ){
log.d("isResume: callback_ref is not set");
log.d( "isResume: callback_ref is not set" );
return false;
}
Callback cb = callback_ref.get();
if( cb == null ){
log.d("isResume: callback was lost.");
log.d( "isResume: callback was lost." );
return false;
}
return cb.isActivityResume();
@ -117,6 +115,8 @@ class Column {
private static final String KEY_WITH_ATTACHMENT = "with_attachment";
private static final String KEY_DONT_SHOW_BOOST = "dont_show_boost";
private static final String KEY_DONT_SHOW_REPLY = "dont_show_reply";
private static final String KEY_DONT_STREAMING = "dont_streaming";
private static final String KEY_DONT_AUTO_REFRESH = "dont_auto_refresh";
private static final String KEY_REGEX_TEXT = "regex_text";
private static final String KEY_HEADER_BACKGROUND_COLOR = "header_background_color";
@ -165,6 +165,9 @@ class Column {
boolean with_attachment;
boolean dont_show_boost;
boolean dont_show_reply;
boolean dont_streaming;
boolean dont_auto_refresh;
String regex_text;
int header_bg_color;
@ -189,7 +192,7 @@ class Column {
ScrollPosition scroll_save;
Column( @NonNull AppState app_state, @NonNull SavedAccount access_info, @NonNull Callback callback,int type, Object... params ){
Column( @NonNull AppState app_state, @NonNull SavedAccount access_info, @NonNull Callback callback, int type, Object... params ){
this.app_state = app_state;
this.context = app_state.context;
this.access_info = access_info;
@ -227,6 +230,8 @@ class Column {
item.put( KEY_WITH_ATTACHMENT, with_attachment );
item.put( KEY_DONT_SHOW_BOOST, dont_show_boost );
item.put( KEY_DONT_SHOW_REPLY, dont_show_reply );
item.put( KEY_DONT_STREAMING, dont_streaming );
item.put( KEY_DONT_AUTO_REFRESH, dont_auto_refresh );
item.put( KEY_REGEX_TEXT, regex_text );
item.put( KEY_HEADER_BACKGROUND_COLOR, header_bg_color );
@ -275,6 +280,8 @@ class Column {
this.with_attachment = src.optBoolean( KEY_WITH_ATTACHMENT );
this.dont_show_boost = src.optBoolean( KEY_DONT_SHOW_BOOST );
this.dont_show_reply = src.optBoolean( KEY_DONT_SHOW_REPLY );
this.dont_streaming = src.optBoolean( KEY_DONT_STREAMING );
this.dont_auto_refresh = src.optBoolean( KEY_DONT_AUTO_REFRESH );
this.regex_text = Utils.optStringX( src, KEY_REGEX_TEXT );
this.header_bg_color = src.optInt( KEY_HEADER_BACKGROUND_COLOR );
@ -887,7 +894,7 @@ class Column {
TootApiResult parseAccount1( TootApiClient client, String path_base ){
TootApiResult result = client.request( path_base );
if( result != null && result.object != null){
if( result != null && result.object != null ){
Column.this.who_account = TootAccount.parse( log, access_info, result.object );
}
return result;
@ -1372,7 +1379,7 @@ class Column {
}
}
if(!bBottom){
if( ! bBottom ){
bRefreshingTop = true;
stopStreaming();
}
@ -1864,7 +1871,7 @@ class Column {
}
}
}finally{
if(!bBottom){
if( ! bBottom ){
bRefreshingTop = false;
resumeStreaming( false );
}
@ -1874,7 +1881,7 @@ class Column {
task.executeOnExecutor( App1.task_executor );
}
void startGap( final TootGap gap, final int position ){
if( last_task != null ){
Utils.showToast( context, true, R.string.column_is_busy );
@ -2297,38 +2304,26 @@ class Column {
return listView.getChildAt( child_idx ).getTop();
}
final StreamReader.Callback stream_callback = new StreamReader.Callback() {
@Override public void onEvent( String event_type, Object o ){
if( o instanceof Long ){
removeStatus( access_info, (Long) o );
return;
}
if( o instanceof TootNotification ){
TootNotification notification = (TootNotification) o;
if( column_type != TYPE_NOTIFICATIONS ) return;
if( isFiltered( notification ) ) return;
}else if( o instanceof TootStatus ){
TootStatus status = (TootStatus) o;
if( column_type == TYPE_NOTIFICATIONS ) return;
if( column_type == TYPE_LOCAL && status.account.acct.indexOf( '@' ) != - 1 ) return;
if( isFiltered( status ) ) return;
}
stream_data_queue.addFirst( o );
proc_stream_data.run();
}
};
////////////////////////////////////////////////////////////////////////
// Streaming
private final LinkedList< Object > stream_data_queue = new LinkedList<>();
private long getId( Object o ){
if( o instanceof TootNotification ){
return ( (TootNotification) o ).id;
}else if( o instanceof TootStatus ){
return ( (TootStatus) o ).id;
}else if( o instanceof TootAccount ){
return ( (TootAccount) o ).id;
}
throw new RuntimeException( "getId: object is not status,notification" );
}
// ListViewの表示更新が追いつかないとスクロール位置が崩れるので
// 一定時間より短期間にはデータ更新しないようにする
private long last_show_stream_data;
private final LinkedList< Object > stream_data_queue = new LinkedList<>();
final Runnable proc_stream_data = new Runnable() {
private final Runnable proc_stream_data = new Runnable() {
@Override public void run(){
App1.getAppState( context ).handler.removeCallbacks( proc_stream_data );
long now = SystemClock.elapsedRealtime();
@ -2341,32 +2336,30 @@ class Column {
ArrayList< Object > list_new = duplicate_map.filterDuplicate( stream_data_queue );
stream_data_queue.clear();
if( list_new.isEmpty() ){
return;
}else{
if( column_type == TYPE_NOTIFICATIONS ){
TootNotification.List list = new TootNotification.List( );
TootNotification.List list = new TootNotification.List();
for( Object o : list_new ){
if( o instanceof TootNotification){
list.add( (TootNotification) o);
if( o instanceof TootNotification ){
list.add( (TootNotification) o );
}
}
if( !list.isEmpty() ){
if( ! list.isEmpty() ){
AlarmService.injectData( context, access_info.db_id, list );
}
}
try{
since_id = Long.toString( getId( list_new.get( 0 ) ) );
}catch(Throwable ex){
}catch( Throwable ex ){
// ストリームに来るのは通知かステータスだから多分ここは通らない
log.e(ex,"getId() failed. o=",list_new.get( 0 ));
log.e( ex, "getId() failed. o=", list_new.get( 0 ) );
}
}
// 事前にスクロール位置を覚えておく
ScrollPosition sp = null;
if( holder != null ){
@ -2394,7 +2387,7 @@ class Column {
long max = getId( list_new.get( list_new.size() - 1 ) );
long since = getId( list_data.get( 0 ) );
if( max > since ){
TootGap gap = new TootGap( max,since );
TootGap gap = new TootGap( max, since );
list_new.add( gap );
}
}
@ -2407,7 +2400,6 @@ class Column {
fireShowContent();
int added = list_new.size();
if( holder != null ){
//noinspection StatementWithEmptyBody
if( sp == null || ( sp.pos == 0 && sp.top == 0 ) ){
@ -2429,20 +2421,32 @@ class Column {
}
};
private long getId( Object o ){
if( o instanceof TootNotification ){
return ( (TootNotification) o ).id;
}else if( o instanceof TootStatus ){
return ( (TootStatus) o ).id;
}else if( o instanceof TootAccount){
return ( (TootAccount) o ).id;
@Override public void onStreamingMessage( String event_type, Object o ){
if( o instanceof Long ){
removeStatus( access_info, (Long) o );
return;
}
throw new RuntimeException( "getId: object is not status,notification" );
if( o instanceof TootNotification ){
TootNotification notification = (TootNotification) o;
if( column_type != TYPE_NOTIFICATIONS ) return;
if( isFiltered( notification ) ) return;
}else if( o instanceof TootStatus ){
TootStatus status = (TootStatus) o;
if( column_type == TYPE_NOTIFICATIONS ) return;
if( column_type == TYPE_LOCAL && status.account.acct.indexOf( '@' ) != - 1 ) return;
if( isFiltered( status ) ) return;
}
stream_data_queue.addFirst( o );
proc_stream_data.run();
}
// onPauseの時はまとめて止められるが
// カラム破棄やリロード開始時は個別にストリーミングを止める必要がある
private void stopStreaming(){
void stopStreaming(){
switch( column_type ){
case TYPE_HOME:
@ -2451,7 +2455,7 @@ class Column {
app_state.stream_reader.unregister(
access_info
, StreamReader.EP_USER
, stream_callback
, this
);
break;
@ -2460,7 +2464,7 @@ class Column {
app_state.stream_reader.unregister(
access_info
, StreamReader.EP_PUBLIC
, stream_callback
, this
);
break;
@ -2468,73 +2472,126 @@ class Column {
app_state.stream_reader.unregister(
access_info
, StreamReader.EP_HASHTAG + "?tag=" + Uri.encode( hashtag )
, stream_callback
, this
);
break;
}
}
void onResume( Callback callback ){
void onResume( Callback callback ){
this.callback_ref = new WeakReference<>( callback );
// 破棄されたカラムなら何もしない
if( is_dispose.get() ){
log.d("onResume: column was disposed.");
log.d( "onResume: column was disposed." );
return;
}
// 未初期化なら何もしない
if( ! bFirstInitialized ){
log.d("onResume: column is not initialized.");
log.d( "onResume: column is not initialized." );
return;
}
// 初期ロード中なら何もしない
if( bInitialLoading ){
log.d("onResume: column is in initial loading.");
log.d( "onResume: column is in initial loading." );
return;
}
if( bRefreshingTop ){
log.d("onResume: bRefreshingTop is true.");
// 始端リフレッシュの最中だった
// リフレッシュ終了時に自動でストリーミング開始するはず
}else if( !bRefreshLoading
&& ! App1.getAppState( context ).pref.getBoolean(Pref.KEY_DONT_REFRESH_ON_RESUME,false)
log.d( "onResume: bRefreshingTop is true." );
}else if(
! bRefreshLoading
&& canAutoRefresh()
&& ! App1.getAppState( context ).pref.getBoolean( Pref.KEY_DONT_REFRESH_ON_RESUME, false )
&& ! dont_auto_refresh
){
log.d("onResume: start auto refresh.");
// リフレッシュしてからストリーミング開始
log.d( "onResume: start auto refresh." );
startRefresh( true, false, - 1L, - 1 );
}else{
log.d("onResume: start streaming with gap.");
// ギャップつきでストリーミング開始
log.d( "onResume: start streaming with gap." );
resumeStreaming( true );
}
}
boolean canAutoRefresh(){
switch( column_type ){
default:
return false;
case TYPE_HOME:
case TYPE_NOTIFICATIONS:
case TYPE_LOCAL:
case TYPE_FEDERATE:
case TYPE_HASHTAG:
return true;
}
}
boolean canStreaming(){
switch( column_type ){
default:
return false;
case TYPE_HOME:
case TYPE_NOTIFICATIONS:
case TYPE_LOCAL:
case TYPE_FEDERATE:
case TYPE_HASHTAG:
return ! access_info.isPseudo();
}
}
private boolean bPutGap;
private void resumeStreaming( boolean bPutGap ){
void resumeStreaming( boolean bPutGap ){
if( ! canStreaming() ){
return;
}
if( ! isResume() ){
log.d("resumeStreaming: not resumed.");
log.d( "resumeStreaming: not resumed." );
return;
}
// 破棄されたカラムなら何もしない
if( is_dispose.get() ){
log.d("resumeStreaming: column was disposed.");
log.d( "resumeStreaming: column was disposed." );
return;
}
// 未初期化なら何もしない
if( ! bFirstInitialized ){
log.d("resumeStreaming: column is not initialized.");
log.d( "resumeStreaming: column is not initialized." );
return;
}
// 初期ロード中なら何もしない
if( bInitialLoading ){
log.d("resumeStreaming: is in initial loading.");
log.d( "resumeStreaming: is in initial loading." );
return;
}
if( App1.getAppState( context ).pref.getBoolean( Pref.KEY_DONT_USE_STREAMING, false ) ){
log.d( "resumeStreaming: disabled in app setting." );
return;
}
if( dont_streaming ){
log.d( "resumeStreaming: disabled in column setting." );
return;
}
if( access_info.isPseudo() ){
log.d( "resumeStreaming: pseudo account can't streaming." );
return;
}
@ -2546,36 +2603,28 @@ class Column {
case TYPE_HOME:
case TYPE_NOTIFICATIONS:
if( access_info.isPseudo() ) return;
app_state.stream_reader.register(
access_info
, StreamReader.EP_USER
, stream_callback
, this
);
break;
case TYPE_LOCAL:
// 認証がないと読めないらしい
if( access_info.isPseudo() ) return;
app_state.stream_reader.register(
access_info
, StreamReader.EP_PUBLIC_LOCAL
, stream_callback
, this
);
break;
case TYPE_FEDERATE:
// 認証がないと読めないらしい
if( access_info.isPseudo() ) return;
app_state.stream_reader.register(
access_info
, StreamReader.EP_PUBLIC
, stream_callback
, this
);
break;
@ -2583,7 +2632,7 @@ class Column {
app_state.stream_reader.register(
access_info
, StreamReader.EP_HASHTAG + "&tag=" + Uri.encode( hashtag )
, stream_callback
, this
);
break;
}

View File

@ -26,7 +26,7 @@ import java.util.regex.Pattern;
import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.view.MyListView;
import jp.juggler.subwaytooter.util.ScrollPosition;
import jp.juggler.subwaytooter.util.Utils;
@ -182,6 +182,24 @@ class ColumnViewHolder
cb.setEnabled( bAllowFilter );
cb.setVisibility( bAllowFilterBoost ? View.VISIBLE : View.GONE );
cb = (CheckBox) root.findViewById( R.id.cbDontStreaming );
if( ! column.canStreaming() ){
cb.setVisibility( View.GONE );
}else{
cb.setVisibility( View.VISIBLE );
cb.setChecked( column.dont_streaming );
cb.setOnCheckedChangeListener( this );
}
cb = (CheckBox) root.findViewById( R.id.cbDontAutoRefresh );
if( ! column.canAutoRefresh() ){
cb.setVisibility( View.GONE );
}else{
cb.setVisibility(View.VISIBLE );
cb.setChecked( column.dont_auto_refresh );
cb.setOnCheckedChangeListener( this );
}
etRegexFilter = (EditText) root.findViewById( R.id.etRegexFilter );
if( ! bAllowFilter ){
etRegexFilter.setVisibility( View.GONE );
@ -415,11 +433,27 @@ class ColumnViewHolder
column.startLoading();
break;
case R.id.cbDontShowReply:
column.dont_show_reply = isChecked;
activity.app_state.saveColumnList();
column.startLoading();
break;
case R.id.cbDontStreaming:
column.dont_streaming = isChecked;
activity.app_state.saveColumnList();
if( isChecked ){
column.onResume( activity );
}else{
column.stopStreaming();
}
break;
case R.id.cbDontAutoRefresh:
column.dont_auto_refresh = isChecked;
activity.app_state.saveColumnList();
break;
}
}

View File

@ -13,8 +13,8 @@ import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.table.UserRelation;
import jp.juggler.subwaytooter.util.Emojione;
import jp.juggler.subwaytooter.util.MyLinkMovementMethod;
import jp.juggler.subwaytooter.util.MyNetworkImageView;
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
import jp.juggler.subwaytooter.view.MyNetworkImageView;
class HeaderViewHolder implements View.OnClickListener, View.OnLongClickListener {
private final Column column;

View File

@ -7,7 +7,7 @@ import android.widget.BaseAdapter;
import java.util.List;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.view.MyListView;
class ItemListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final Column column;

View File

@ -18,10 +18,10 @@ import jp.juggler.subwaytooter.table.ContentWarning;
import jp.juggler.subwaytooter.table.MediaShown;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.table.UserRelation;
import jp.juggler.subwaytooter.util.MyLinkMovementMethod;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.util.MyNetworkImageView;
import jp.juggler.subwaytooter.util.MyTextView;
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
import jp.juggler.subwaytooter.view.MyListView;
import jp.juggler.subwaytooter.view.MyNetworkImageView;
import jp.juggler.subwaytooter.view.MyTextView;
import jp.juggler.subwaytooter.util.Utils;
class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {

View File

@ -11,7 +11,7 @@ import android.view.WindowManager;
import android.widget.PopupWindow;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.view.MyListView;
class StatusButtonsPopup {

View File

@ -34,7 +34,7 @@ class StreamReader {
static final String EP_HASHTAG = "/api/v1/streaming/?stream=hashtag"; // + &tag=hashtag (先頭のを含まない)
interface Callback {
void onEvent( String event_type, Object o );
void onStreamingMessage( String event_type, Object o );
}
private class Reader extends WebSocketListener {
@ -100,7 +100,7 @@ class StreamReader {
synchronized( this ){
for( Callback callback : callback_list ){
try{
callback.onEvent( event, payload );
callback.onStreamingMessage( event, payload );
}catch( Throwable ex ){
ex.printStackTrace();
}
@ -269,8 +269,6 @@ class StreamReader {
// onResume ロード完了ののタイミングで登録される
void register( @NonNull SavedAccount access_info, @NonNull String end_point, @NonNull Callback stream_callback ){
if( pref.getBoolean( Pref.KEY_DONT_USE_STREAMING ,false) ) return;
final Reader reader = prepareReader( access_info, end_point );
reader.addCallback( stream_callback );

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.api;
import java.util.ArrayList;
import java.util.Collection;

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.dialog;
import android.content.Context;
import android.content.DialogInterface;

View File

@ -84,12 +84,8 @@ public class HTMLDecoder {
}
}
public interface LinkClickCallback {
void onClickLink( LinkClickContext account, String url );
}
public static LinkClickCallback link_callback;
private static final boolean DEBUG_HTML_PARSER = false;
private static class Node {

View File

@ -6,7 +6,13 @@ import android.view.View;
import jp.juggler.subwaytooter.table.AcctColor;
final class MyClickableSpan extends ClickableSpan {
public class MyClickableSpan extends ClickableSpan {
public interface LinkClickCallback {
void onClickLink( LinkClickContext account, String url );
}
public static LinkClickCallback link_callback;
public LinkClickContext account;
public String url;
@ -23,8 +29,8 @@ final class MyClickableSpan extends ClickableSpan {
}
@Override public void onClick( View widget ){
if( HTMLDecoder.link_callback != null ){
HTMLDecoder.link_callback.onClickLink( this.account, url );
if( link_callback != null ){
link_callback.onClickLink( this.account, url );
}
}

View File

@ -1,5 +1,7 @@
package jp.juggler.subwaytooter.util;
import jp.juggler.subwaytooter.view.MyListView;
public class ScrollPosition {
public int pos;
public int top;

View File

@ -0,0 +1,59 @@
package jp.juggler.subwaytooter.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ScrollView;
import jp.juggler.subwaytooter.R;
public class MaxHeightScrollView extends ScrollView {
public MaxHeightScrollView( Context context ){
super( context );
}
public MaxHeightScrollView( Context context, AttributeSet attrs ){
super( context, attrs );
TypedArray a = context.obtainStyledAttributes(attrs , R.styleable.MaxHeightScrollView);
parseAttr(a);
a.recycle();
}
public MaxHeightScrollView( Context context, AttributeSet attrs, int defStyleAttr ){
super( context, attrs, defStyleAttr );
TypedArray a = context.obtainStyledAttributes(attrs
, R.styleable.MaxHeightScrollView, defStyleAttr, 0);
parseAttr(a);
a.recycle();
}
int maxHeight;
void parseAttr( TypedArray a){
maxHeight = a.getDimensionPixelSize( R.styleable.MaxHeightScrollView_maxHeight, 0);
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (maxHeight > 0){
int hSize = MeasureSpec.getSize(heightMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
switch (hMode){
case MeasureSpec.AT_MOST:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.AT_MOST);
break;
case MeasureSpec.UNSPECIFIED:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
break;
case MeasureSpec.EXACTLY:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.EXACTLY);
break;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.view;
import android.content.Context;
import android.support.v7.widget.AppCompatEditText;

View File

@ -1,8 +1,7 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.view;
import android.support.v4.view.MotionEventCompat;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.method.Touch;

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.view;
import android.content.Context;
import android.os.SystemClock;

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.view;
import android.content.Context;
import android.content.SharedPreferences;

View File

@ -1,4 +1,4 @@
package jp.juggler.subwaytooter.util;
package jp.juggler.subwaytooter.view;
import android.content.Context;
import android.support.annotation.Nullable;

View File

@ -54,7 +54,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivReply"
android:layout_width="40dp"
android:layout_height="40dp"
@ -119,7 +119,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia1"
android:layout_width="48dp"
android:layout_height="match_parent"
@ -127,7 +127,7 @@
android:scaleType="fitCenter"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia2"
android:layout_width="48dp"
android:layout_height="match_parent"
@ -136,7 +136,7 @@
android:scaleType="fitCenter"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia3"
android:layout_width="48dp"
android:layout_height="match_parent"
@ -145,7 +145,7 @@
android:scaleType="fitCenter"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia4"
android:layout_width="48dp"
android:layout_height="match_parent"
@ -179,7 +179,7 @@
android:background="?attr/colorPostFormBackground"
>
<jp.juggler.subwaytooter.util.MyEditText
<jp.juggler.subwaytooter.view.MyEditText
android:id="@+id/etContentWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -201,7 +201,7 @@
android:background="?attr/colorPostFormBackground"
>
<jp.juggler.subwaytooter.util.MyEditText
<jp.juggler.subwaytooter.view.MyEditText
android:id="@+id/etContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -19,7 +19,7 @@
android:orientation="vertical"
>
<jp.juggler.subwaytooter.util.MyEditText
<jp.juggler.subwaytooter.view.MyEditText
android:id="@+id/etText"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -23,7 +23,7 @@
android:layout_height="wrap_content"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -39,7 +39,7 @@
android:padding="12dp"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivAvatar"
android:layout_width="128dp"
android:layout_height="128dp"
@ -69,7 +69,7 @@
/>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvNote"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -90,7 +90,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivFollow"
android:layout_width="48dp"
android:layout_height="40dp"
@ -204,7 +204,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivThumbnail"
android:layout_width="48dp"
android:layout_height="48dp"
@ -258,13 +258,13 @@
android:orientation="vertical"
>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvMentions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvContentWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -282,7 +282,7 @@
android:orientation="vertical"
>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -304,7 +304,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia1"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -314,7 +314,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia2"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -325,7 +325,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia3"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -336,7 +336,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia4"
android:layout_width="0dp"
android:layout_height="match_parent"

View File

@ -89,7 +89,7 @@
android:layout_marginBottom="4dp"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivFollow"
android:layout_width="48dp"
android:layout_height="40dp"
@ -202,7 +202,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivThumbnail"
android:layout_width="48dp"
android:layout_height="48dp"
@ -255,13 +255,13 @@
android:orientation="vertical"
>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvMentions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvContentWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -279,7 +279,7 @@
android:orientation="vertical"
>
<jp.juggler.subwaytooter.util.MyTextView
<jp.juggler.subwaytooter.view.MyTextView
android:id="@+id/tvContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -301,7 +301,7 @@
android:orientation="horizontal"
>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia1"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -311,7 +311,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia2"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -322,7 +322,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia3"
android:layout_width="0dp"
android:layout_height="match_parent"
@ -333,7 +333,7 @@
android:scaleType="centerCrop"
/>
<jp.juggler.subwaytooter.util.MyNetworkImageView
<jp.juggler.subwaytooter.view.MyNetworkImageView
android:id="@+id/ivMedia4"
android:layout_width="0dp"
android:layout_height="match_parent"

View File

@ -13,17 +13,17 @@
android:layout_height="wrap_content"
android:background="@drawable/btn_bg_ddd"
android:orientation="vertical"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="bottom"
android:orientation="horizontal"
>
<TextView
@ -32,12 +32,12 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:paddingEnd="4dp"
android:paddingStart="4dp"
android:textColor="?attr/colorColumnHeaderAcct"
android:textSize="12sp"
tools:text="tvColumnContext"
android:paddingEnd="4dp"
android:paddingStart="4dp"
/>
/>
<TextView
android:id="@+id/tvColumnIndex"
@ -60,12 +60,13 @@
>
<ImageView
android:id="@+id/ivColumnIcon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="4dp"
android:id="@+id/ivColumnIcon"
android:importantForAccessibility="no"
/>
<TextView
android:id="@+id/tvColumnName"
android:layout_width="0dp"
@ -112,97 +113,119 @@
</LinearLayout>
<LinearLayout
<jp.juggler.subwaytooter.view.MaxHeightScrollView
android:id="@+id/llColumnSetting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorColumnSettingBackground"
android:orientation="vertical"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
app:maxHeight="240dp"
android:fadeScrollbars="false"
>
<CheckBox
android:id="@+id/cbDontCloseColumn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_close_column"
/>
<CheckBox
android:id="@+id/cbWithAttachment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/with_attachment"
/>
<CheckBox
android:id="@+id/cbDontShowBoost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_boost"
/>
<CheckBox
android:id="@+id/cbDontShowReply"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_reply"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/llRegexFilter"
android:background="?attr/colorColumnSettingBackground"
android:orientation="vertical"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
>
<TextView
android:layout_width="wrap_content"
<CheckBox
android:id="@+id/cbDontCloseColumn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@+id/etRegexFilter"
android:text="@string/regex_filter"
android:textColor="?attr/colorColumnHeaderPageNumber"
android:text="@string/dont_close_column"
/>
<TextView
android:id="@+id/tvRegexFilterError"
android:layout_width="0dp"
<CheckBox
android:id="@+id/cbWithAttachment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:textColor="?attr/colorRegexFilterError"
android:text="@string/with_attachment"
/>
<CheckBox
android:id="@+id/cbDontShowBoost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_boost"
/>
<CheckBox
android:id="@+id/cbDontShowReply"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_reply"
/>
<CheckBox
android:id="@+id/cbDontStreaming"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_use_streaming_api"
/>
<CheckBox
android:id="@+id/cbDontAutoRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_refresh_on_activity_resume"
/>
<LinearLayout
android:id="@+id/llRegexFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/etRegexFilter"
android:text="@string/regex_filter"
android:textColor="?attr/colorColumnHeaderPageNumber"
/>
<TextView
android:id="@+id/tvRegexFilterError"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:textColor="?attr/colorRegexFilterError"
/>
</LinearLayout>
<EditText
android:id="@+id/etRegexFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
android:scrollHorizontally="true"
/>
<Button
android:id="@+id/btnDeleteNotification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/notification_delete"
android:textAllCaps="false"
/>
<Button
android:id="@+id/btnColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/color_and_background"
android:textAllCaps="false"
/>
</LinearLayout>
<EditText
android:id="@+id/etRegexFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:scrollHorizontally="true"
android:inputType="text"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/notification_delete"
android:id="@+id/btnDeleteNotification"
android:textAllCaps="false"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/color_and_background"
android:id="@+id/btnColor"
android:textAllCaps="false"
/>
</LinearLayout>
</jp.juggler.subwaytooter.view.MaxHeightScrollView>
<RelativeLayout
android:id="@+id/llSearch"
@ -246,10 +269,10 @@
</RelativeLayout>
<FrameLayout
android:id="@+id/flColumnBackground"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/flColumnBackground"
>
<ImageView
@ -273,19 +296,19 @@
android:layout_height="match_parent"
app:srl_direction="both">
<jp.juggler.subwaytooter.util.MyListView
<jp.juggler.subwaytooter.view.MyListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:divider="?attr/colorSettingDivider"
android:dividerHeight="1dp"
android:fadeScrollbars="false"
android:fastScrollEnabled="true"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
android:dividerHeight="1dp"
android:divider="?attr/colorSettingDivider"
/>
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>

View File

@ -589,7 +589,7 @@
<string name="open_status_from">ステータスを次のアカウントから開く</string>
<string name="help_translation">翻訳に協力する</string>
<string name="dont_use_streaming_api">ストリーミングAPIを使わない</string>
<string name="dont_refresh_on_activity_resume">画面復帰時に自動ロードしない</string>
<string name="dont_refresh_on_activity_resume">画面復帰時に新着データを取得しない</string>
<string name="already_boosted">既にブースト済みです</string>
<string name="already_favourited">既にお気に入り済みです</string>
<string name="status_id_conversion_failed">ステータスIDを変換できませんでした</string>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
</resources>