From af2fade5767c7dacabddebb9e2097da17e75fc9f Mon Sep 17 00:00:00 2001 From: tateisu Date: Fri, 12 May 2017 23:10:02 +0900 Subject: [PATCH] =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88=E3=81=9A?= =?UTF-8?q?=E3=83=A1=E3=82=A4=E3=83=B3=E7=94=BB=E9=9D=A2=E3=81=AE=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=A0=E3=81=91=E3=82=BF=E3=83=96=E3=83=AC=E3=83=83?= =?UTF-8?q?=E3=83=88=E5=AF=BE=E5=BF=9C=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/jp/juggler/subwaytooter/ActMain.java | 538 ++++++++++++----- .../java/jp/juggler/subwaytooter/Column.java | 10 + .../subwaytooter/ColumnPagerAdapter.java | 42 +- .../subwaytooter/ColumnViewHolder.java | 3 +- .../juggler/subwaytooter/DlgContextMenu.java | 22 +- .../subwaytooter/HeaderViewHolder.java | 9 +- .../juggler/subwaytooter/ItemViewHolder.java | 24 +- .../juggler/subwaytooter/StatusButtons.java | 4 +- .../TabletColumnPagerAdapter.java | 54 ++ .../subwaytooter/TabletColumnViewHolder.java | 37 ++ .../subwaytooter/util/MyClickableSpan.java | 4 +- .../subwaytooter/view/GravitySnapHelper.java | 174 ++++++ .../subwaytooter/view/MyRecyclerView.java | 81 +++ .../view/TabletColumnDivider.java | 50 ++ app/src/main/res/layout/act_main.xml | 4 + app/src/main/res/layout/page_column.xml | 563 +++++++++--------- 16 files changed, 1132 insertions(+), 487 deletions(-) create mode 100644 app/src/main/java/jp/juggler/subwaytooter/TabletColumnPagerAdapter.java create mode 100644 app/src/main/java/jp/juggler/subwaytooter/TabletColumnViewHolder.java create mode 100644 app/src/main/java/jp/juggler/subwaytooter/view/GravitySnapHelper.java create mode 100644 app/src/main/java/jp/juggler/subwaytooter/view/MyRecyclerView.java create mode 100644 app/src/main/java/jp/juggler/subwaytooter/view/TabletColumnDivider.java diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java index 0adf6e76..8ae34797 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java @@ -16,7 +16,10 @@ import android.support.v4.text.BidiFormatter; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.support.design.widget.NavigationView; @@ -38,6 +41,7 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -58,11 +62,11 @@ import jp.juggler.subwaytooter.table.MutedApp; import jp.juggler.subwaytooter.table.SavedAccount; import jp.juggler.subwaytooter.table.UserRelation; 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 jp.juggler.subwaytooter.view.GravitySnapHelper; import okhttp3.Request; import okhttp3.RequestBody; @@ -88,8 +92,7 @@ public class ActMain extends AppCompatActivity String posted_acct; long posted_status_id; - @Override - protected void onCreate( Bundle savedInstanceState ){ + @Override protected void onCreate( Bundle savedInstanceState ){ super.onCreate( savedInstanceState ); App1.setActivityTheme( this, true ); requestWindowFeature( Window.FEATURE_NO_TITLE ); @@ -105,9 +108,13 @@ public class ActMain extends AppCompatActivity updateColumnStrip(); - if( pager_adapter.column_list.size() > 0 ){ + if( app_state.column_list.size() > 0 ){ llEmpty.setVisibility( View.GONE ); - onPageSelected( pager.getCurrentItem() ); + if( pager_adapter != null ){ + onPageSelected( pager.getCurrentItem() ); + }else{ + resizeColumnWidth(); + } } AlarmService.startCheck( this ); @@ -127,13 +134,13 @@ public class ActMain extends AppCompatActivity bResume = true; log.d( "onResume" ); super.onResume(); - + MyClickableSpan.link_callback = link_click_listener; - if( pref.getBoolean( Pref.KEY_DONT_SCREEN_OFF,false )){ - getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + if( pref.getBoolean( Pref.KEY_DONT_SCREEN_OFF, false ) ){ + getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ); }else{ - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + getWindow().clearFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ); } // アカウント設定から戻ってきたら、カラムを消す必要があるかもしれない @@ -141,8 +148,8 @@ public class ActMain extends AppCompatActivity ArrayList< Integer > new_order = new ArrayList<>(); boolean bRemoved = false; - for( int i = 0, ie = pager_adapter.getCount() ; i < ie ; ++ i ){ - Column column = pager_adapter.getColumn( i ); + for( int i = 0, ie = app_state.column_list.size() ; i < ie ; ++ i ){ + Column column = app_state.column_list.get( i ); SavedAccount sa = SavedAccount.loadAccount( log, column.access_info.db_id ); if( sa == null ){ bRemoved = true; @@ -151,9 +158,8 @@ public class ActMain extends AppCompatActivity } } if( bRemoved ){ - pager_adapter.setOrder( pager, new_order ); - app_state.saveColumnList(); - updateColumnStrip(); + setOrder( new_order ); + } } @@ -163,7 +169,7 @@ public class ActMain extends AppCompatActivity if( ! TextUtils.isEmpty( posted_acct ) ){ int refresh_after_toot = pref.getInt( Pref.KEY_REFRESH_AFTER_TOOT, 0 ); if( refresh_after_toot != Pref.RAT_DONT_REFRESH ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ SavedAccount a = column.access_info; if( ! Utils.equalsNullable( a.acct, posted_acct ) ) continue; column.startRefreshForPost( posted_status_id, refresh_after_toot ); @@ -182,7 +188,7 @@ public class ActMain extends AppCompatActivity handleSentIntent( intent ); } - if( pager_adapter.getCount() == 0 ){ + if( app_state.column_list.isEmpty() ){ llEmpty.setVisibility( View.VISIBLE ); }else{ for( Column column : app_state.column_list ){ @@ -191,6 +197,8 @@ public class ActMain extends AppCompatActivity } } + + private void handleSentIntent( final Intent sent_intent ){ AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() { @Override public void onAccountPicked( @NonNull SavedAccount ai ){ @@ -261,7 +269,7 @@ public class ActMain extends AppCompatActivity } boolean isOrderChanged( ArrayList< Integer > new_order ){ - if( new_order.size() != pager_adapter.getCount() ) return true; + if( new_order.size() != app_state.column_list.size() ) return true; for( int i = 0, ie = new_order.size() ; i < ie ; ++ i ){ if( new_order.get( i ) != i ) return true; } @@ -283,18 +291,16 @@ public class ActMain extends AppCompatActivity if( data != null ){ ArrayList< Integer > order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER ); if( order != null && isOrderChanged( order ) ){ - pager_adapter.setOrder( pager, order ); - app_state.saveColumnList(); - updateColumnStrip(); + setOrder( order ); + } - if( pager_adapter.column_list.isEmpty() ){ + if( app_state.column_list.isEmpty() ){ llEmpty.setVisibility( View.VISIBLE ); }else{ int select = data.getIntExtra( ActColumnList.EXTRA_SELECTION, - 1 ); - if( 0 <= select && select < pager_adapter.getCount() ){ - pager.setCurrentItem( select, true ); - scrollColumnStrip( select ); + if( 0 <= select && select < app_state.column_list.size() ){ + scrollToColumn( select); } } } @@ -307,12 +313,12 @@ public class ActMain extends AppCompatActivity if( data != null ){ String search = data.getStringExtra( ActAbout.EXTRA_SEARCH ); if( ! TextUtils.isEmpty( search ) ){ - performAddTimeline( true, Column.TYPE_SEARCH, search, true ); + performAddTimeline( getDefaultInsertPosition(), true, Column.TYPE_SEARCH, search, true ); } return; } }else if( requestCode == REQUEST_CODE_NICKNAME ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.onNicknameUpdated(); } }else if( requestCode == REQUEST_CODE_POST ){ @@ -324,13 +330,11 @@ public class ActMain extends AppCompatActivity if( data != null ){ app_state.saveColumnList(); int idx = data.getIntExtra( ActColumnCustomize.EXTRA_COLUMN_INDEX, 0 ); - ColumnViewHolder vh = pager_adapter.getColumnViewHolder( idx ); - if( vh != null ){ - vh.showColumnColor(); + if( idx >= 0 && idx < app_state.column_list.size() ){ + app_state.column_list.get(idx).fireColumnColor(); } updateColumnStrip(); } - } } @@ -341,6 +345,8 @@ public class ActMain extends AppCompatActivity super.onActivityResult( requestCode, resultCode, data ); } + + @Override public void onBackPressed(){ @@ -352,15 +358,13 @@ public class ActMain extends AppCompatActivity } // カラムが0個ならアプリを終了する - if( pager_adapter.getCount() == 0 ){ + if( app_state.column_list.isEmpty() ){ ActMain.this.finish(); return; } // カラム設定が開いているならカラム設定を閉じる - ColumnViewHolder vh = pager_adapter.getColumnViewHolder( pager.getCurrentItem() ); - if( vh.isColumnSettingShown() ){ - vh.closeColumnSetting(); + if( closeColumnSetting() ){ return; } @@ -369,11 +373,17 @@ public class ActMain extends AppCompatActivity default: case ActAppSetting.BACK_ASK_ALWAYS: ActionsDialog dialog = new ActionsDialog(); + dialog.addAction( getString( R.string.close_column ), new Runnable() { @Override public void run(){ - closeColumn( true, pager_adapter.getColumn( pager.getCurrentItem() ) ); + if( pager_adapter != null ){ + closeColumn( true, pager_adapter.getColumn( pager.getCurrentItem() ) ); + }else{ + // TODO + } } } ); + dialog.addAction( getString( R.string.open_column_list ), new Runnable() { @Override public void run(){ openColumnList(); @@ -388,16 +398,20 @@ public class ActMain extends AppCompatActivity break; case ActAppSetting.BACK_CLOSE_COLUMN: - Column column = pager_adapter.getColumn( pager.getCurrentItem() ); - if( column != null ){ - if( column.dont_close - && pref.getBoolean( Pref.KEY_EXIT_APP_WHEN_CLOSE_PROTECTED_COLUMN, false ) - && pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, false ) - ){ - ActMain.this.finish(); - return; + if( pager_adapter != null ){ + Column column = pager_adapter.getColumn( pager.getCurrentItem() ); + if( column != null ){ + if( column.dont_close + && pref.getBoolean( Pref.KEY_EXIT_APP_WHEN_CLOSE_PROTECTED_COLUMN, false ) + && pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, false ) + ){ + ActMain.this.finish(); + return; + } + closeColumn( false, column ); } - closeColumn( false, column ); + }else{ + // TODO } break; @@ -411,6 +425,8 @@ public class ActMain extends AppCompatActivity } } + + @Override public boolean onCreateOptionsMenu( Menu menu ){ // Inflate the menu; this adds items to the action bar if it is present. @@ -443,21 +459,21 @@ public class ActMain extends AppCompatActivity performAccountAdd(); }else if( id == R.id.nav_add_tl_home ){ - performAddTimeline( false, Column.TYPE_HOME ); + performAddTimeline(getDefaultInsertPosition(), false, Column.TYPE_HOME ); }else if( id == R.id.nav_add_tl_local ){ - performAddTimeline( true, Column.TYPE_LOCAL ); + performAddTimeline( getDefaultInsertPosition(),true, Column.TYPE_LOCAL ); }else if( id == R.id.nav_add_tl_federate ){ - performAddTimeline( true, Column.TYPE_FEDERATE ); + performAddTimeline( getDefaultInsertPosition(),true, Column.TYPE_FEDERATE ); }else if( id == R.id.nav_add_favourites ){ - performAddTimeline( false, Column.TYPE_FAVOURITES ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_FAVOURITES ); // }else if( id == R.id.nav_add_reports ){ // performAddTimeline(Column.TYPE_REPORTS ); }else if( id == R.id.nav_add_statuses ){ - performAddTimeline( false, Column.TYPE_PROFILE ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_PROFILE ); }else if( id == R.id.nav_add_notifications ){ - performAddTimeline( false, Column.TYPE_NOTIFICATIONS ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_NOTIFICATIONS ); }else if( id == R.id.nav_app_setting ){ ActAppSetting.open( this, REQUEST_APP_SETTING ); @@ -469,7 +485,7 @@ public class ActMain extends AppCompatActivity openColumnList(); }else if( id == R.id.nav_add_tl_search ){ - performAddTimeline( true, Column.TYPE_SEARCH, "", false ); + performAddTimeline( getDefaultInsertPosition(), true, Column.TYPE_SEARCH, "", false ); }else if( id == R.id.nav_app_about ){ openAppAbout(); @@ -481,13 +497,13 @@ public class ActMain extends AppCompatActivity finish(); }else if( id == R.id.nav_add_mutes ){ - performAddTimeline( false, Column.TYPE_MUTES ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_MUTES ); }else if( id == R.id.nav_add_blocks ){ - performAddTimeline( false, Column.TYPE_BLOCKS ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_BLOCKS ); }else if( id == R.id.nav_follow_requests ){ - performAddTimeline( false, Column.TYPE_FOLLOW_REQUESTS ); + performAddTimeline( getDefaultInsertPosition(),false, Column.TYPE_FOLLOW_REQUESTS ); }else if( id == R.id.nav_muted_app ){ startActivity( new Intent( this, ActMutedApp.class ) ); @@ -529,6 +545,10 @@ public class ActMain extends AppCompatActivity View vFooterDivider1; View vFooterDivider2; + RecyclerView tablet_pager; + TabletColumnPagerAdapter tablet_pager_adapter; + LinearLayoutManager tablet_layout_manager; + void initUI(){ setContentView( R.layout.act_main ); @@ -560,11 +580,38 @@ public class ActMain extends AppCompatActivity svColumnStrip.setHorizontalFadingEdgeEnabled( true ); - // ViewPager + DisplayMetrics dm = getResources().getDisplayMetrics(); + // + int sw = dm.widthPixels; + // int sh = dm.heightPixels; + // int short_side = ( sw < sh ? sw : sh ); + // + // float density = dm.density; + // int column_w_min = (int) ( 0.5f + 320f * density ); + pager = (ViewPager) findViewById( R.id.viewPager ); - pager_adapter = new ColumnPagerAdapter( this ); - pager.setAdapter( pager_adapter ); - pager.addOnPageChangeListener( this ); + tablet_pager = (RecyclerView) findViewById( R.id.rvPager ); + + //noinspection ConstantIfStatement + if( false ){ + tablet_pager.setVisibility( View.GONE ); + // SmartPhone mode + pager_adapter = new ColumnPagerAdapter( this ); + pager.setAdapter( pager_adapter ); + pager.addOnPageChangeListener( this ); + }else{ + pager.setVisibility( View.GONE ); + + // tablet mode + tablet_pager_adapter = new TabletColumnPagerAdapter( this ); + tablet_layout_manager = new LinearLayoutManager( this, LinearLayoutManager.HORIZONTAL, false ); + tablet_pager.setAdapter( tablet_pager_adapter ); + tablet_pager.setLayoutManager( tablet_layout_manager ); + ///////tablet_pager.setHasFixedSize( true ); + // tablet_pager.addItemDecoration( new TabletColumnDivider( this ) ); + + new GravitySnapHelper(Gravity.START).attachToRecyclerView( tablet_pager ); + } showFooterColor(); } @@ -581,7 +628,7 @@ public class ActMain extends AppCompatActivity viewRoot.setTag( i ); viewRoot.setOnClickListener( new View.OnClickListener() { @Override public void onClick( View v ){ - onClickColumnStrip( (Integer) v.getTag() ); + scrollToColumn( (Integer) v.getTag() ); } } ); viewRoot.setContentDescription( column.getColumnName( true ) ); @@ -647,9 +694,7 @@ public class ActMain extends AppCompatActivity } - private void onClickColumnStrip( int idx ){ - pager.setCurrentItem( idx, true ); - } + public void performAccountAdd(){ LoginForm.showLoginForm( this, null, new LoginForm.LoginFormCallback() { @@ -714,7 +759,8 @@ public class ActMain extends AppCompatActivity if( a != null ){ // 疑似アカウントが追加された Utils.showToast( ActMain.this, false, R.string.server_confirmed ); - addColumn( a, Column.TYPE_LOCAL ); + int pos = app_state.column_list.size(); + addColumn( pos, a, Column.TYPE_LOCAL ); dialog.dismiss(); } @@ -815,7 +861,7 @@ public class ActMain extends AppCompatActivity , new AccountPicker.AccountPickerCallback() { @Override public void onAccountPicked( @NonNull final SavedAccount ai ){ - openStatus( ai, status_id ); + openStatus( getDefaultInsertPosition(),ai, status_id ); } } ); @@ -858,10 +904,10 @@ public class ActMain extends AppCompatActivity startGetAccount( ai, host, user, new GetAccountCallback() { @Override public void onGetAccount( TootAccount who ){ if( who != null ){ - performOpenUser( ai, who ); + performOpenUser(getDefaultInsertPosition(), ai, who ); return; } - openChromeTab( ai, uri.toString(), true ); + openChromeTab( getDefaultInsertPosition(),ai, uri.toString(), true ); } } ); } @@ -883,7 +929,7 @@ public class ActMain extends AppCompatActivity long db_id = Long.parseLong( sv, 10 ); SavedAccount account = SavedAccount.loadAccount( log, db_id ); if( account != null ){ - Column column = addColumn( account, Column.TYPE_NOTIFICATIONS ); + Column column = addColumn( getDefaultInsertPosition(), account, Column.TYPE_NOTIFICATIONS ); if( ! column.bInitialLoading ){ column.startLoading(); } @@ -1007,7 +1053,7 @@ public class ActMain extends AppCompatActivity reloadAccountSetting(); // 自動でリロードする - for( Column c : pager_adapter.column_list ){ + for( Column c : app_state.column_list ){ if( c.access_info.acct.equals( sa.acct ) ){ c.startLoading(); } @@ -1025,7 +1071,7 @@ public class ActMain extends AppCompatActivity } Utils.showToast( ActMain.this, false, R.string.account_confirmed ); AlarmService.startCheck( ActMain.this ); - addColumn( account, Column.TYPE_HOME ); + addColumn( getDefaultInsertPosition(),account, Column.TYPE_HOME ); } } } @@ -1044,7 +1090,7 @@ public class ActMain extends AppCompatActivity void reloadAccountSetting(){ ArrayList< SavedAccount > done_list = new ArrayList<>(); - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ SavedAccount a = column.access_info; if( done_list.contains( a ) ) continue; done_list.add( a ); @@ -1055,7 +1101,7 @@ public class ActMain extends AppCompatActivity void reloadAccountSetting( SavedAccount account ){ ArrayList< SavedAccount > done_list = new ArrayList<>(); - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ SavedAccount a = column.access_info; if( ! Utils.equalsNullable( a.acct, account.acct ) ) continue; if( done_list.contains( a ) ) continue; @@ -1084,72 +1130,88 @@ public class ActMain extends AppCompatActivity .show(); return; } - int page_showing = pager.getCurrentItem(); - int page_delete = pager_adapter.column_list.indexOf( column ); - pager_adapter.removeColumn( pager, column ); - app_state.saveColumnList(); - updateColumnStrip(); - if( pager_adapter.getCount() == 0 ){ - llEmpty.setVisibility( View.VISIBLE ); - }else if( page_showing > 0 && page_showing == page_delete ){ - pager.setCurrentItem( page_showing - 1, true ); - scrollColumnStrip( page_showing - 1 ); + + int page_delete = app_state.column_list.indexOf( column ); + + if( pager_adapter != null){ + int page_showing = pager.getCurrentItem(); + + removeColumn( column ); + + + if( app_state.column_list.isEmpty() ){ + llEmpty.setVisibility( View.VISIBLE ); + }else if( page_delete > 0 && page_showing == page_delete ){ + scrollToColumn( page_showing - 1 ); + } + + }else{ + removeColumn( column ); + + if( app_state.column_list.isEmpty() ){ + llEmpty.setVisibility( View.VISIBLE ); + }else{ + // TODO 一つ左のカラムが見えるようにしたい + if( page_delete > 0 ){ + scrollToColumn( page_delete -1 ); + } + } } } ////////////////////////////////////////////////////////////// // カラム追加系 - public Column addColumn( SavedAccount ai, int type, Object... params ){ + public Column addColumn( int index, SavedAccount ai, int type, Object... params ){ // 既に同じカラムがあればそこに移動する - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ if( column.isSameSpec( ai, type, params ) ){ - pager.setCurrentItem( pager_adapter.column_list.indexOf( column ), true ); + index = app_state.column_list.indexOf( column ); + scrollToColumn( index ); return column; } } + // llEmpty.setVisibility( View.GONE ); // Column col = new Column( app_state, ai, this, type, params ); - int idx = pager_adapter.addColumn( pager, col ); - app_state.saveColumnList(); - updateColumnStrip(); - pager.setCurrentItem( idx, true ); + index = addColumn( col, index ); + scrollToColumn( index ); return col; } - void performOpenUser( @NonNull SavedAccount access_info, @NonNull TootAccount user ){ + void performOpenUser( int pos,@NonNull SavedAccount access_info, @NonNull TootAccount user ){ if( access_info.isPseudo() ){ Utils.showToast( this, false, R.string.not_available_for_pseudo_account ); }else{ - addColumn( access_info, Column.TYPE_PROFILE, user.id ); + addColumn( pos,access_info, Column.TYPE_PROFILE, user.id ); } } - public void performOpenUserFromAnotherAccount( final TootAccount who, ArrayList< SavedAccount > account_list_non_pseudo_same_instance ){ + public void performOpenUserFromAnotherAccount( final int pos, final TootAccount who, ArrayList< SavedAccount > account_list_non_pseudo_same_instance ){ AccountPicker.pick( this, false, false , getString( R.string.account_picker_open_user_who, AcctColor.getNickname( who.acct ) ) , account_list_non_pseudo_same_instance , new AccountPicker.AccountPickerCallback() { @Override public void onAccountPicked( @NonNull SavedAccount ai ){ - addColumn( ai, Column.TYPE_PROFILE, who.id ); + addColumn( pos,ai, Column.TYPE_PROFILE, who.id ); } } ); } - private void performAddTimeline( boolean bAllowPseudo, final int type, final Object... args ){ + private void performAddTimeline( final int pos, boolean bAllowPseudo, final int type, final Object... args ){ AccountPicker.pick( this, bAllowPseudo, true , getString( R.string.account_picker_add_timeline_of, Column.getColumnTypeName( this, type ) ) , new AccountPicker.AccountPickerCallback() { @Override public void onAccountPicked( @NonNull SavedAccount ai ){ switch( type ){ default: - addColumn( ai, type, args ); + addColumn( pos,ai, type, args ); break; case Column.TYPE_PROFILE: - addColumn( ai, type, ai.id ); + addColumn(pos, ai, type, ai.id ); break; } } @@ -1158,7 +1220,7 @@ public class ActMain extends AppCompatActivity public void performMuteApp( @NonNull TootApplication application ){ MutedApp.save( application.name ); - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.removeMuteApp(); } Utils.showToast( ActMain.this, false, R.string.app_was_muted ); @@ -1245,7 +1307,7 @@ public class ActMain extends AppCompatActivity static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)\\z" ); static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)/(\\d+)\\z" ); - public void openChromeTab( final SavedAccount access_info, final String url, boolean noIntercept ){ + public void openChromeTab( final int pos,final SavedAccount access_info, final String url, boolean noIntercept ){ try{ log.d( "openChromeTab url=%s", url ); @@ -1257,10 +1319,10 @@ public class ActMain extends AppCompatActivity String host = m.group( 1 ); String tag = Uri.decode( m.group( 2 ) ); if( host.equalsIgnoreCase( access_info.host ) ){ - openHashTag( access_info, tag ); + openHashTag( pos,access_info, tag ); return; }else{ - openHashTagOtherInstance( access_info, url, host, tag ); + openHashTagOtherInstance( pos,access_info, url, host, tag ); return; } } @@ -1273,10 +1335,10 @@ public class ActMain extends AppCompatActivity final String host = m.group( 1 ); final long status_id = Long.parseLong( m.group( 3 ), 10 ); if( host.equalsIgnoreCase( access_info.host ) ){ - openStatus( access_info, status_id ); + openStatus(pos, access_info, status_id ); return; }else{ - openStatusOtherInstance( access_info, url, host, status_id ); + openStatusOtherInstance( pos,access_info, url, host, status_id ); return; } }catch( Throwable ex ){ @@ -1294,10 +1356,10 @@ public class ActMain extends AppCompatActivity startGetAccount( access_info, host, user, new GetAccountCallback() { @Override public void onGetAccount( TootAccount who ){ if( who != null ){ - performOpenUser( access_info, who ); + performOpenUser( pos,access_info, who ); return; } - openChromeTab( access_info, url, true ); + openChromeTab( pos,access_info, url, true ); } } ); return; @@ -1329,21 +1391,21 @@ public class ActMain extends AppCompatActivity } } - public void openStatus( @NonNull SavedAccount access_info, @NonNull TootStatus status ){ - openStatus( access_info, status.id ); + public void openStatus( int pos,@NonNull SavedAccount access_info, @NonNull TootStatus status ){ + openStatus( pos,access_info, status.id ); } - public void openStatus( @NonNull SavedAccount access_info, long status_id ){ - addColumn( access_info, Column.TYPE_CONVERSATION, status_id ); + public void openStatus( int pos, @NonNull SavedAccount access_info, long status_id ){ + addColumn( pos,access_info, Column.TYPE_CONVERSATION, status_id ); } - private void openStatusOtherInstance( final SavedAccount access_info, final String url, final String host, final long status_id ){ + private void openStatusOtherInstance( final int pos,final SavedAccount access_info, final String url, final String host, final long status_id ){ ActionsDialog dialog = new ActionsDialog(); // ブラウザで表示する dialog.addAction( getString( R.string.open_web_on_host, host ), new Runnable() { @Override public void run(){ - openChromeTab( access_info, url, true ); + openChromeTab( pos,access_info, url, true ); } } ); @@ -1366,7 +1428,7 @@ public class ActMain extends AppCompatActivity final SavedAccount _a = a; dialog.addAction( getString( R.string.open_in_account, a.acct ), new Runnable() { @Override public void run(){ - openStatus( _a, status_id ); + openStatus( pos, _a, status_id ); } } ); } @@ -1377,7 +1439,7 @@ public class ActMain extends AppCompatActivity @Override public void run(){ SavedAccount sa = addPseudoAccount( host ); if( sa != null ){ - openStatus( sa, status_id ); + openStatus( pos,sa, status_id ); } } } ); @@ -1386,19 +1448,19 @@ public class ActMain extends AppCompatActivity dialog.show( this, getString( R.string.open_status_from ) ); } - public void openHashTag( SavedAccount access_info, String tag ){ - addColumn( access_info, Column.TYPE_HASHTAG, tag ); + public void openHashTag( int pos, SavedAccount access_info, String tag ){ + addColumn( pos, access_info, Column.TYPE_HASHTAG, tag ); } // 他インスタンスのハッシュタグの表示 - private void openHashTagOtherInstance( final SavedAccount access_info, final String url, final String host, final String tag ){ + private void openHashTagOtherInstance( final int pos, final SavedAccount access_info, final String url, final String host, final String tag ){ ActionsDialog dialog = new ActionsDialog(); // ブラウザで表示する dialog.addAction( getString( R.string.open_web_on_host, host ), new Runnable() { @Override public void run(){ - openChromeTab( access_info, url, true ); + openChromeTab( pos,access_info, url, true ); } } ); @@ -1423,7 +1485,7 @@ public class ActMain extends AppCompatActivity final SavedAccount _a = a; dialog.addAction( getString( R.string.open_in_account, a.acct ), new Runnable() { @Override public void run(){ - openHashTag( _a, tag ); + openHashTag( pos, _a, tag ); } } ); } @@ -1433,7 +1495,7 @@ public class ActMain extends AppCompatActivity @Override public void run(){ SavedAccount sa = addPseudoAccount( host ); if( sa != null ){ - openHashTag( sa, tag ); + openHashTag( pos,sa, tag ); } } } ); @@ -1444,20 +1506,43 @@ public class ActMain extends AppCompatActivity } final MyClickableSpan.LinkClickCallback link_click_listener = new MyClickableSpan.LinkClickCallback() { - @Override public void onClickLink( LinkClickContext lcc, String url ){ - openChromeTab( (SavedAccount) lcc, url, false ); + @Override public void onClickLink( View view,LinkClickContext lcc, String url ){ + Column column = null; + while( view != null ){ + Object tag = view.getTag(); + if( tag instanceof ItemViewHolder ){ + column = ((ItemViewHolder)tag).column; + break; + }else if( tag instanceof HeaderViewHolder ){ + column = ( (HeaderViewHolder) tag ).column; + break; + }else if( tag instanceof TabletColumnViewHolder ){ + column = ( (TabletColumnViewHolder) tag ).vh.column; + break; + }else{ + view = (View) view.getParent(); + } + } + + openChromeTab( nextPosition( column ),(SavedAccount) lcc, url, false ); } }; + + private void performTootButton(){ - Column c = pager_adapter.getColumn( pager.getCurrentItem() ); - if( c != null ){ - if( c.access_info.isPseudo() ){ - Utils.showToast( this, false, R.string.not_available_for_pseudo_account ); - }else{ + if( pager_adapter != null){ + Column c = pager_adapter.getColumn( pager.getCurrentItem() ); + if( c != null && ! c.access_info.isPseudo() ){ ActPost.open( this, REQUEST_CODE_POST, c.access_info.db_id, "" ); } } + + AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() { + @Override public void onAccountPicked( @NonNull SavedAccount ai ){ + ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, "" ); + } + } ); } public void performReply( SavedAccount account, TootStatus status ){ @@ -1482,7 +1567,7 @@ public class ActMain extends AppCompatActivity ///////////////////////////////////////////////////////////////////////// private void showColumnMatchAccount( SavedAccount account ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ if( account.acct.equals( column.access_info.acct ) ){ column.fireShowContent(); } @@ -1603,7 +1688,7 @@ public class ActMain extends AppCompatActivity } } - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.findStatus( access_info, new_status.id, new Column.StatusEntryCallback() { @Override public void onIterate( TootStatus status ){ @@ -1768,7 +1853,7 @@ public class ActMain extends AppCompatActivity } } } - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.findStatus( access_info, new_status.id, new Column.StatusEntryCallback() { @Override public void onIterate( TootStatus status ){ status.reblogged = new_status.reblogged; @@ -1804,16 +1889,13 @@ public class ActMain extends AppCompatActivity // column list private void openColumnList(){ - ActColumnList.open( this, pager.getCurrentItem(), REQUEST_CODE_COLUMN_LIST ); + if( pager_adapter != null ){ + ActColumnList.open( this, pager.getCurrentItem(), REQUEST_CODE_COLUMN_LIST ); + }else{ + ActColumnList.open( this, -1, REQUEST_CODE_COLUMN_LIST ); + } } - // private void dumpColumnList(){ - // for( int i = 0, ie = pager_adapter.column_list.size() ; i < ie ; ++ i ){ - // Column column = pager_adapter.column_list.get( i ); - // log.d( "dumpColumnList [%s]%s %s", i, column.access_info.acct, column.getColumnName( true ) ); - // } - // } - //////////////////////////////////////////////////////////////////////////// interface RelationChangedCallback { @@ -1827,8 +1909,7 @@ public class ActMain extends AppCompatActivity } // relationshipを取得 - @NonNull - RelationResult loadRelation1( TootApiClient client, SavedAccount access_info, long who_id ){ + @NonNull RelationResult loadRelation1( TootApiClient client, SavedAccount access_info, long who_id ){ RelationResult rr = new RelationResult(); TootApiResult r2 = rr.result = client.request( "/api/v1/accounts/relationships?id=" + who_id ); if( r2 != null && r2.array != null ){ @@ -2206,11 +2287,11 @@ public class ActMain extends AppCompatActivity Utils.showToast( ActMain.this, false, bMute ? R.string.mute_succeeded : R.string.unmute_succeeded ); if( bMute ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.removeStatusByAccount( access_info, who.id ); } }else{ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.removeFromMuteList( access_info, who.id ); } } @@ -2274,7 +2355,7 @@ public class ActMain extends AppCompatActivity // cancelled. }else if( relation != null ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ if( bBlock ){ column.removeStatusByAccount( access_info, who.id ); }else{ @@ -2331,7 +2412,7 @@ public class ActMain extends AppCompatActivity // cancelled. }else if( result.object != null ){ - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.removeFollowRequest( access_info, who.id ); } @@ -2378,7 +2459,7 @@ public class ActMain extends AppCompatActivity //cancelled. }else if( result.object != null ){ Utils.showToast( ActMain.this, false, R.string.delete_succeeded ); - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ column.removeStatus( access_info, status_id ); } }else{ @@ -2547,7 +2628,7 @@ public class ActMain extends AppCompatActivity //cancelled. }else if( result.object != null ){ // ok. empty object will be returned. - for( Column column : pager_adapter.column_list ){ + for( Column column : app_state.column_list ){ if( column.column_type == Column.TYPE_NOTIFICATIONS && column.access_info.acct.equals( target_account.acct ) ){ @@ -2626,12 +2707,12 @@ public class ActMain extends AppCompatActivity if( status == null ) return; AccountPicker.pick( this, false, false , getString( R.string.account_picker_boost ) - , makeAccountListNonPseudo(log) + , makeAccountListNonPseudo( log ) , new AccountPicker.AccountPickerCallback() { @Override public void onAccountPicked( @NonNull SavedAccount ai ){ performBoost( ai - , !ai.host.equalsIgnoreCase( access_info.host ) + , ! ai.host.equalsIgnoreCase( access_info.host ) , true , status , false @@ -2676,4 +2757,171 @@ public class ActMain extends AppCompatActivity } } ); } + + + private boolean closeColumnSetting(){ + if( pager_adapter != null){ + ColumnViewHolder vh = pager_adapter.getColumnViewHolder( pager.getCurrentItem() ); + if( vh.isColumnSettingShown() ){ + vh.closeColumnSetting(); + return true; + } + }else{ + for( int i = 0, ie = tablet_layout_manager.getChildCount() ; i < ie ; ++ i ){ + View v = tablet_layout_manager.getChildAt( i ); + Object o = v.getTag(); + if( o instanceof TabletColumnViewHolder ){ + TabletColumnViewHolder holder = (TabletColumnViewHolder) o; + if( holder.vh.isColumnSettingShown() ){ + holder.vh.closeColumnSetting(); + return true; + } + } + } + } + return false; + } + + + + private int getDefaultInsertPosition(){ + if( pager_adapter != null ){ + return 1+ pager.getCurrentItem(); + }else{ + return Integer.MAX_VALUE; + } + } + + int nextPosition( Column column ){ + if( column != null ){ + int pos = app_state.column_list.indexOf( column ); + if( pos != - 1 ) return pos + 1; + } + return getDefaultInsertPosition(); + } + + + private int addColumn( Column column, int index ){ + int size = app_state.column_list.size(); + if( index > size ) index = size; + + if( pager_adapter != null){ + pager.setAdapter( null ); + app_state.column_list.add( index, column ); + pager.setAdapter( pager_adapter ); + }else{ + app_state.column_list.add( index, column ); + resizeColumnWidth(); + } + + app_state.saveColumnList(); + updateColumnStrip(); + + return index; + + } + + private void removeColumn( Column column ){ + int idx_column = app_state.column_list.indexOf( column ); + if( idx_column == - 1 ) return; + + if( pager_adapter != null){ + pager.setAdapter( null ); + app_state.column_list.remove( idx_column ).dispose(); + pager.setAdapter( pager_adapter ); + }else{ + app_state.column_list.remove( idx_column ).dispose(); + resizeColumnWidth(); + } + + app_state.saveColumnList(); + updateColumnStrip(); + } + + private void setOrder( ArrayList< Integer > new_order ){ + if( pager_adapter != null ){ + pager.setAdapter( null ); + } + + ArrayList< Column > tmp_list = new ArrayList<>(); + HashSet< Integer > used_set = new HashSet<>(); + + for( Integer i : new_order ){ + used_set.add( i ); + tmp_list.add( app_state.column_list.get( i ) ); + } + + for( int i = 0, ie = app_state.column_list.size() ; i < ie ; ++ i ){ + if( used_set.contains( i ) ) continue; + app_state.column_list.get( i ).dispose(); + } + app_state.column_list.clear(); + app_state.column_list.addAll( tmp_list ); + + if( pager_adapter != null ){ + pager.setAdapter( pager_adapter ); + }else{ + resizeColumnWidth(); + } + + app_state.saveColumnList(); + updateColumnStrip(); + } + + int nScreenColumn; + + private void resizeColumnWidth(){ + // TODO カラム幅を調節する + DisplayMetrics dm = getResources().getDisplayMetrics(); + // + int sw = dm.widthPixels; + // int sh = dm.heightPixels; + // int short_side = ( sw < sh ? sw : sh ); + // + float density = dm.density; + int column_w_min = (int) ( 0.5f + 320f * density ); + int column_w_max = (int) ( 0.5f + 360f * density ); + + if( sw >= column_w_min * 2 ){ + nScreenColumn = sw / column_w_min; + if( nScreenColumn > app_state.column_list.size() ){ + nScreenColumn = app_state.column_list.size(); + } + int column_w = sw/ nScreenColumn; + if( column_w > column_w_max ){ + column_w = column_w_max; + } + tablet_pager_adapter.setColumnWidth( column_w ); + }else{ + tablet_pager_adapter.setColumnWidth( sw ); + } + + // 並べ直す + tablet_pager_adapter.notifyDataSetChanged(); + } + + private void scrollToColumn( int index ){ + scrollColumnStrip( index ); + + if( pager_adapter != null ){ + pager.setCurrentItem( index, true ); + }else{ + // 指定されたカラムが画面内に表示される最低限の移動ですませたい + if( nScreenColumn >0 ){ + int vs = tablet_layout_manager.findFirstVisibleItemPosition(); + if( vs != RecyclerView.NO_POSITION ){ + if( index < vs ){ + tablet_pager.smoothScrollToPosition( index ); + }else{ + int ve = vs + nScreenColumn - 1; + if( index > ve ){ + tablet_pager.smoothScrollToPosition( index - ( nScreenColumn - 1 ) ); + } + } + } + } + } + } + + } diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java index 3842de70..9deb3145 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java @@ -714,6 +714,11 @@ class Column implements StreamReader.Callback { if( holder != null ) holder.showColumnHeader(); } }; + private final Runnable proc_showColumnColor = new Runnable() { + @Override public void run(){ + if( holder != null ) holder.showColumnColor(); + } + }; void fireShowContent(){ Utils.runOnMainThread( proc_showContent ); @@ -724,6 +729,11 @@ class Column implements StreamReader.Callback { Utils.runOnMainThread( proc_showColumnHeader ); } + void fireColumnColor(){ + Utils.runOnMainThread( proc_showColumnColor ); + } + + private AsyncTask< Void, Void, TootApiResult > last_task; private void cancelLastTask(){ diff --git a/app/src/main/java/jp/juggler/subwaytooter/ColumnPagerAdapter.java b/app/src/main/java/jp/juggler/subwaytooter/ColumnPagerAdapter.java index 2084d964..6fb7d3ed 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ColumnPagerAdapter.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ColumnPagerAdapter.java @@ -37,46 +37,6 @@ class ColumnPagerAdapter extends PagerAdapter { return holder_list.get( idx ); } - int addColumn( ViewPager pager, Column column ){ - return addColumn( pager, column, pager.getCurrentItem() + 1 ); - } - - int addColumn( ViewPager pager, Column column, int index ){ - int size = column_list.size(); - if( index > size ) index = size; - pager.setAdapter( null ); - column_list.add( index, column ); - pager.setAdapter( this ); - return index; - } - - void removeColumn( ViewPager pager, Column column ){ - int idx_column = column_list.indexOf( column ); - if( idx_column == - 1 ) return; - pager.setAdapter( null ); - column_list.remove( idx_column ).dispose(); - pager.setAdapter( this ); - } - - void setOrder( ViewPager pager, ArrayList< Integer > order ){ - pager.setAdapter( null ); - - ArrayList< Column > tmp_list = new ArrayList<>(); - HashSet< Integer > used_set = new HashSet<>(); - - for( Integer i : order ){ - used_set.add( i ); - tmp_list.add( column_list.get( i ) ); - } - for( int i = 0, ie = column_list.size() ; i < ie ; ++ i ){ - if( used_set.contains( i ) ) continue; - column_list.get( i ).dispose(); - } - column_list.clear(); - column_list.addAll( tmp_list ); - - pager.setAdapter( this ); - } @Override public CharSequence getPageTitle( int page_idx ){ return getColumn( page_idx).getColumnName( false ); @@ -108,7 +68,7 @@ class ColumnPagerAdapter extends PagerAdapter { ColumnViewHolder holder = holder_list.get( page_idx ); holder_list.remove( page_idx ); if( holder != null ){ - holder.is_destroyed.set( true ); + holder.onPageDestroy( view ); } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.java index fb849723..7f736c63 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.java @@ -54,7 +54,8 @@ class ColumnViewHolder void onPageDestroy( @SuppressWarnings("UnusedParameters") View root ){ log.d( "onPageDestroy:%s", column.getColumnName( true ) ); - + is_destroyed.set( true ); + saveScrollPosition(); column.setColumnViewHolder( null ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/DlgContextMenu.java b/app/src/main/java/jp/juggler/subwaytooter/DlgContextMenu.java index d7c6e42a..167614c7 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/DlgContextMenu.java +++ b/app/src/main/java/jp/juggler/subwaytooter/DlgContextMenu.java @@ -33,6 +33,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { @NonNull private final TootAccount who; @Nullable private final TootStatus status; @NonNull private final UserRelation relation; + @NonNull private final Column column; private final Dialog dialog; @@ -41,15 +42,16 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { DlgContextMenu( @NonNull ActMain activity - , @NonNull SavedAccount access_info + , @NonNull Column column , @NonNull TootAccount who , @Nullable TootStatus status - , int column_type ){ this.activity = activity; - this.access_info = access_info; + this.column = column; + this.access_info = column.access_info; this.who = who; this.status = status; + int column_type = column.column_type; this.relation = UserRelation.load( access_info.db_id, who.id ); @@ -235,11 +237,13 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { dialog.dismiss(); + int pos = activity.nextPosition( column ) ; + switch( v.getId() ){ case R.id.btnStatusWebPage: if( status != null ){ - activity.openChromeTab( access_info, status.url, true ); + activity.openChromeTab(pos,access_info, status.url, true ); } break; @@ -318,13 +322,13 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { case R.id.btnBoostedBy: if( status != null ){ - activity.addColumn( access_info, Column.TYPE_BOOSTED_BY, status.id ); + activity.addColumn( pos,access_info, Column.TYPE_BOOSTED_BY, status.id ); } break; case R.id.btnFavouritedBy: if( status != null ){ - activity.addColumn( access_info, Column.TYPE_FAVOURITED_BY, status.id ); + activity.addColumn( pos,access_info, Column.TYPE_FAVOURITED_BY, status.id ); } break; @@ -371,7 +375,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { break; case R.id.btnProfile: - activity.performOpenUser( access_info, who ); + activity.performOpenUser( pos,access_info, who ); break; case R.id.btnSendMessage: @@ -379,7 +383,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { break; case R.id.btnAccountWebPage: - activity.openChromeTab( access_info, who.url, true ); + activity.openChromeTab( pos, access_info, who.url, true ); break; case R.id.btnFollowRequestOK: @@ -399,7 +403,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener { break; case R.id.btnOpenProfileFromAnotherAccount: - activity.performOpenUserFromAnotherAccount( who, account_list_non_pseudo_same_instance ); + activity.performOpenUserFromAnotherAccount( pos,who, account_list_non_pseudo_same_instance ); break; case R.id.btnNickname: diff --git a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolder.java index 321fcd61..b060cdc3 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolder.java @@ -17,7 +17,7 @@ import jp.juggler.subwaytooter.view.MyLinkMovementMethod; import jp.juggler.subwaytooter.view.MyNetworkImageView; class HeaderViewHolder implements View.OnClickListener, View.OnLongClickListener { - private final Column column; + final Column column; private final ActMain activity; private final SavedAccount access_info; @@ -106,12 +106,13 @@ class HeaderViewHolder implements View.OnClickListener, View.OnLongClickListener @Override public void onClick( View v ){ + switch( v.getId() ){ case R.id.ivBackground: if( who != null ){ // 強制的にブラウザで開く - activity.openChromeTab( access_info, who.url, true ); + activity.openChromeTab( activity.nextPosition( column ), access_info, who.url, true ); } break; @@ -135,13 +136,13 @@ class HeaderViewHolder implements View.OnClickListener, View.OnLongClickListener case R.id.btnMore: if( who != null ){ - new DlgContextMenu( activity, access_info, who, null, column.column_type ).show(); + new DlgContextMenu( activity, column, who, null ).show(); } break; case R.id.btnFollow: if( who != null ){ - new DlgContextMenu( activity, access_info, who, null, column.column_type ).show(); + new DlgContextMenu( activity, column, who, null ).show(); } break; diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java index ccced4f9..1d027a67 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java @@ -385,6 +385,9 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { } @Override public void onClick( View v ){ + + int pos = activity.nextPosition( column ) ; + switch( v.getId() ){ case R.id.btnHideMedia: MediaShown.save( access_info.host, status.id, false ); @@ -415,33 +418,33 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { case R.id.ivThumbnail: if( access_info.isPseudo() ){ - new DlgContextMenu( activity, access_info, account_thumbnail, null, column.column_type ).show(); + new DlgContextMenu( activity, column, account_thumbnail, null ).show(); }else{ - activity.performOpenUser( access_info, account_thumbnail ); + activity.performOpenUser( pos,access_info, account_thumbnail ); } break; case R.id.llBoosted: if( access_info.isPseudo() ){ - new DlgContextMenu( activity, access_info, account_boost, null, column.column_type ).show(); + new DlgContextMenu( activity, column, account_boost, null ).show(); }else{ - activity.performOpenUser( access_info, account_boost ); + activity.performOpenUser( pos,access_info, account_boost ); } break; case R.id.llFollow: if( access_info.isPseudo() ){ - new DlgContextMenu( activity, access_info, account_follow, null, column.column_type ).show(); + new DlgContextMenu( activity, column, account_follow, null ).show(); }else{ - activity.performOpenUser( access_info, account_follow ); + activity.performOpenUser( pos,access_info, account_follow ); } break; case R.id.btnFollow: - new DlgContextMenu( activity, access_info, account_follow, null, column.column_type ).show(); + new DlgContextMenu( activity, column, account_follow, null ).show(); break; case R.id.btnSearchTag: if( search_tag != null ){ - activity.openHashTag( access_info, search_tag ); + activity.openHashTag( pos,access_info, search_tag ); }else if( gap != null ){ column.startGap( gap, position ); } @@ -453,7 +456,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { switch( v.getId() ){ case R.id.ivThumbnail: - new DlgContextMenu( activity, access_info, account_thumbnail, null, column.column_type ).show(); + new DlgContextMenu( activity, column, account_thumbnail, null ).show(); return true; case R.id.btnFollow: @@ -481,7 +484,8 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { sv = a.url; } } - activity.openChromeTab( access_info, sv, false ); + int pos = activity.nextPosition( column ) ; + activity.openChromeTab(pos, access_info, sv, false ); }catch( Throwable ex ){ ex.printStackTrace(); } diff --git a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java index 8cbb2ff4..071142c2 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java +++ b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java @@ -113,7 +113,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener { if( close_window != null ) close_window.dismiss(); switch( v.getId() ){ case R.id.btnConversation: - activity.openStatus( access_info, status ); + activity.openStatus( activity.nextPosition( column ), access_info, status ); break; case R.id.btnReply: if( access_info.isPseudo() ){ @@ -152,7 +152,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener { break; case R.id.btnMore: - new DlgContextMenu( activity, access_info, status.account, status, column.column_type ).show(); + new DlgContextMenu( activity, column, status.account, status ).show(); break; case R.id.btnFollow2: diff --git a/app/src/main/java/jp/juggler/subwaytooter/TabletColumnPagerAdapter.java b/app/src/main/java/jp/juggler/subwaytooter/TabletColumnPagerAdapter.java new file mode 100644 index 00000000..951091d2 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/TabletColumnPagerAdapter.java @@ -0,0 +1,54 @@ +package jp.juggler.subwaytooter; + +import android.support.v4.view.ViewPager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +class TabletColumnPagerAdapter extends RecyclerView.Adapter{ + + private final ActMain activity; + private final LayoutInflater mLayoutInflater; + private final List column_list; + + TabletColumnPagerAdapter( ActMain activity ){ + super(); + this.activity = activity; + this.column_list = activity.app_state.column_list; + mLayoutInflater = LayoutInflater.from( activity ); + } + + @Override public int getItemCount(){ + return column_list.size(); + } + + private int mColumnWidth = 0; + + void setColumnWidth( int width ){ + mColumnWidth = width; + } + + @Override public TabletColumnViewHolder onCreateViewHolder( ViewGroup parent, int viewType ){ + View v = mLayoutInflater.inflate( R.layout.page_column, parent, false ); + + return new TabletColumnViewHolder( v ); + } + + @Override public void onBindViewHolder( TabletColumnViewHolder holder, int position ){ + + if( mColumnWidth > 0 ){ + ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + lp.width = mColumnWidth; + holder.itemView.setLayoutParams( lp ); + } + + holder.bind( activity, column_list.get(position), position , column_list.size() ); + } + + + + +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/TabletColumnViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/TabletColumnViewHolder.java new file mode 100644 index 00000000..a5c0820b --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/TabletColumnViewHolder.java @@ -0,0 +1,37 @@ +package jp.juggler.subwaytooter; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import jp.juggler.subwaytooter.util.LogCategory; + +class TabletColumnViewHolder extends RecyclerView.ViewHolder{ + static final LogCategory log = new LogCategory( "TabletColumnViewHolder" ); + + + ColumnViewHolder vh; + private int old_position; + + TabletColumnViewHolder( View v ){ + super( v ); + + v.findViewById( R.id.vTabletDivider ).setVisibility( View.VISIBLE ); + } + + void bind( ActMain activity, Column column,int position,int column_count ){ + if( vh != null ){ + log.d("destroy #%s",old_position); + vh.onPageDestroy( itemView ); + vh = null; + } + + old_position = position; + log.d("create #%s",position); + vh =new ColumnViewHolder( activity, column); + vh.onPageCreate( itemView,position,column_count ); + + if( ! column.bFirstInitialized ){ + column.startLoading(); + } + } +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java b/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java index 215582a3..54f15966 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java +++ b/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java @@ -9,7 +9,7 @@ import jp.juggler.subwaytooter.table.AcctColor; public class MyClickableSpan extends ClickableSpan { public interface LinkClickCallback { - void onClickLink( LinkClickContext account, String url ); + void onClickLink( View widget,LinkClickContext account, String url ); } public static LinkClickCallback link_callback; @@ -30,7 +30,7 @@ public class MyClickableSpan extends ClickableSpan { @Override public void onClick( View widget ){ if( link_callback != null ){ - link_callback.onClickLink( this.account, url ); + link_callback.onClickLink( widget,this.account, url ); } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/view/GravitySnapHelper.java b/app/src/main/java/jp/juggler/subwaytooter/view/GravitySnapHelper.java new file mode 100644 index 00000000..da509d5c --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/view/GravitySnapHelper.java @@ -0,0 +1,174 @@ +package jp.juggler.subwaytooter.view; + +import android.annotation.SuppressLint; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSnapHelper; +import android.support.v7.widget.OrientationHelper; +import android.support.v7.widget.RecyclerView; +import android.view.Gravity; +import android.view.View; + +/** + * Created by tateisu on 2017/05/12. + */ + +public class GravitySnapHelper extends LinearSnapHelper { + + private OrientationHelper verticalHelper; + private OrientationHelper horizontalHelper; + private int gravity; + private boolean isRTL; + + @SuppressLint("RtlHardcoded") + public GravitySnapHelper(int gravity) { + this.gravity = gravity; + if (this.gravity == Gravity.LEFT) { + this.gravity = Gravity.START; + } else if (this.gravity == Gravity.RIGHT) { + this.gravity = Gravity.END; + } + } + + @Override + public void attachToRecyclerView(@Nullable RecyclerView recyclerView) + throws IllegalStateException { + if (recyclerView != null) { + isRTL = ViewCompat.getLayoutDirection(recyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL; + } + super.attachToRecyclerView(recyclerView); + } + + @Override + public int[] calculateDistanceToFinalSnap( + @NonNull RecyclerView.LayoutManager layoutManager + ,@NonNull View targetView + ) { + int[] out = new int[2]; + + if (layoutManager.canScrollHorizontally()) { + if (gravity == Gravity.START) { + out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); + } else { // END + out[0] = distanceToEnd(targetView, getHorizontalHelper(layoutManager)); + } + } else { + out[0] = 0; + } + + if (layoutManager.canScrollVertically()) { + if (gravity == Gravity.TOP) { + out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager)); + } else { // BOTTOM + out[1] = distanceToEnd(targetView, getVerticalHelper(layoutManager)); + } + } else { + out[1] = 0; + } + return out; + } + + @Override + public View findSnapView(RecyclerView.LayoutManager layoutManager) { + if (layoutManager instanceof LinearLayoutManager ) { + switch (gravity) { + case Gravity.START: + return findStartView(layoutManager, getHorizontalHelper(layoutManager)); + case Gravity.TOP: + return findStartView(layoutManager, getVerticalHelper(layoutManager)); + case Gravity.END: + return findEndView(layoutManager, getHorizontalHelper(layoutManager)); + case Gravity.BOTTOM: + return findEndView(layoutManager, getVerticalHelper(layoutManager)); + } + } + + return super.findSnapView(layoutManager); + } + + private int distanceToStart(View targetView, OrientationHelper helper) { + if ( isRTL ) { + return distanceToEnd(targetView, helper); + } + return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); + } + + private int distanceToEnd(View targetView, OrientationHelper helper) { + if ( isRTL ) { + return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); + } + return helper.getDecoratedEnd(targetView) - helper.getEndAfterPadding(); + } + + private View findStartView(RecyclerView.LayoutManager layoutManager, + OrientationHelper helper) { + + if (layoutManager instanceof LinearLayoutManager) { + int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + + if (firstChild == RecyclerView.NO_POSITION) { + return null; + } + + View child = layoutManager.findViewByPosition(firstChild); + + if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 + && helper.getDecoratedEnd(child) > 0) { + return child; + } else { + if (((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition() + == layoutManager.getItemCount() - 1) { + return null; + } else { + return layoutManager.findViewByPosition(firstChild + 1); + } + } + } + + return super.findSnapView(layoutManager); + } + + private View findEndView(RecyclerView.LayoutManager layoutManager, + OrientationHelper helper) { + + if (layoutManager instanceof LinearLayoutManager) { + int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); + + if (lastChild == RecyclerView.NO_POSITION) { + return null; + } + + View child = layoutManager.findViewByPosition(lastChild); + + if (helper.getDecoratedStart(child) + helper.getDecoratedMeasurement(child) / 2 + <= helper.getTotalSpace()) { + return child; + } else { + if (((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition() + == 0) { + return null; + } else { + return layoutManager.findViewByPosition(lastChild - 1); + } + } + } + + return super.findSnapView(layoutManager); + } + + private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) { + if (verticalHelper == null) { + verticalHelper = OrientationHelper.createVerticalHelper(layoutManager); + } + return verticalHelper; + } + + private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) { + if (horizontalHelper == null) { + horizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); + } + return horizontalHelper; + } +} \ No newline at end of file diff --git a/app/src/main/java/jp/juggler/subwaytooter/view/MyRecyclerView.java b/app/src/main/java/jp/juggler/subwaytooter/view/MyRecyclerView.java new file mode 100644 index 00000000..30b8a17a --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/view/MyRecyclerView.java @@ -0,0 +1,81 @@ +package jp.juggler.subwaytooter.view; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.support.v4.view.MotionEventCompat; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +public class MyRecyclerView extends RecyclerView { + + public MyRecyclerView( Context context ){ + super( context ); + init(context); + } + + + public MyRecyclerView( Context context, @Nullable AttributeSet attrs ){ + super( context, attrs ); + init(context); + } + + public MyRecyclerView( Context context, @Nullable AttributeSet attrs, int defStyle ){ + super( context, attrs, defStyle ); + init(context); + + } + + private void init( Context context ){ + final ViewConfiguration vc = ViewConfiguration.get(context); + mTouchSlop = vc.getScaledTouchSlop(); + } + + + boolean mForbidStartDragging; + int mScrollPointerId; + int mInitialTouchX; + int mInitialTouchY; + int mTouchSlop; + + @Override + public boolean onInterceptTouchEvent( MotionEvent e ){ + final int action = MotionEventCompat.getActionMasked( e ); + // final int actionIndex = MotionEventCompat.getActionIndex( e ); + + switch( action ){ + + case MotionEvent.ACTION_DOWN: + mForbidStartDragging = false; + mScrollPointerId = e.getPointerId(0); + mInitialTouchX = (int) (e.getX() + 0.5f); + mInitialTouchY = (int) (e.getY() + 0.5f); + + break; + + case MotionEvent.ACTION_MOVE: + final int index = e.findPointerIndex( mScrollPointerId ); + if( index >= 0 ){ + if( mForbidStartDragging ) return false; + + final int x = (int) ( e.getX( index ) + 0.5f ); + final int y = (int) ( e.getY( index ) + 0.5f ); + boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally(); + boolean canScrollVertically = getLayoutManager().canScrollVertically(); + + final int dx = x - mInitialTouchX; + final int dy = y - mInitialTouchY; + + if( ( ! canScrollVertically && Math.abs( dy ) > mTouchSlop ) + || ( ! canScrollHorizontally && Math.abs( dx ) > mTouchSlop ) + ){ + mForbidStartDragging = true; + return false; + } + } + break; + } + return super.onInterceptTouchEvent( e ); + } +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/view/TabletColumnDivider.java b/app/src/main/java/jp/juggler/subwaytooter/view/TabletColumnDivider.java new file mode 100644 index 00000000..bc894212 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/view/TabletColumnDivider.java @@ -0,0 +1,50 @@ +//package jp.juggler.subwaytooter.view; +// +//import android.content.Context; +//import android.content.res.TypedArray; +//import android.graphics.Canvas; +//import android.graphics.Paint; +//import android.graphics.Rect; +//import android.support.v7.widget.RecyclerView; +//import android.view.View; +// +//import jp.juggler.subwaytooter.R; +//import jp.juggler.subwaytooter.Styler; +// +//public class TabletColumnDivider extends RecyclerView.ItemDecoration { +// +// final Paint paint = new Paint(); +// int width; +// +// public TabletColumnDivider(Context context) { +// +// width = (int)(0.5f + 1f * context.getResources().getDisplayMetrics().density); +// int color = Styler.getAttributeColor(context, R.attr.colorSettingDivider); +// +// paint.setColor( color ); +// } +// +// @Override public void onDraw( Canvas c, RecyclerView parent, RecyclerView.State state) { +// drawDivider(c, parent); +// } +// +// void drawDivider(Canvas c, RecyclerView parent) { +// final int t = parent.getPaddingTop(); +// final int b = parent.getHeight() - parent.getPaddingBottom(); +// +// final int childCount = parent.getChildCount(); +// for (int i = 0; i < childCount; i++) { +// final View child = parent.getChildAt(i); +// final int r = child.getRight(); +// final int l = r - width; +// c.drawRect( l,t,r,b,paint ); +// +// } +// } +// +// @Override +// public void getItemOffsets( Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { +// outRect.set(width,0,width,0); +// } +// +//} diff --git a/app/src/main/res/layout/act_main.xml b/app/src/main/res/layout/act_main.xml index 8e180ea2..30bc7357 100644 --- a/app/src/main/res/layout/act_main.xml +++ b/app/src/main/res/layout/act_main.xml @@ -46,6 +46,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + - + > + android:layout_height="match_parent" + android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - -