Merge branch 'master' into patch-2

This commit is contained in:
tateisu 2017-05-15 08:34:26 +09:00 committed by GitHub
commit 667c811013
17 changed files with 670 additions and 465 deletions

View File

@ -10,12 +10,13 @@ import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.v4.text.BidiFormatter; import android.support.v4.text.BidiFormatter;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewParentCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@ -655,8 +656,6 @@ public class ActMain extends AppCompatActivity
int sw = dm.widthPixels; int sw = dm.widthPixels;
pager = (ViewPager) findViewById( R.id.viewPager ); pager = (ViewPager) findViewById( R.id.viewPager );
tablet_pager = (RecyclerView) findViewById( R.id.rvPager ); tablet_pager = (RecyclerView) findViewById( R.id.rvPager );
@ -914,9 +913,10 @@ public class ActMain extends AppCompatActivity
// プロフURL // プロフURL
if( "https".equals( uri.getScheme() ) ){ if( "https".equals( uri.getScheme() ) ){
if( uri.getPath().startsWith( "/@" ) ){ if( uri.getPath().startsWith( "/@" ) ){
// ステータスをアプリ内で開く
Matcher m = reStatusPage.matcher( uri.toString() ); Matcher m = reStatusPage.matcher( uri.toString() );
if( m.find() ){ if( m.find() ){
// ステータスをアプリ内で開く
try{ try{
// https://mastodon.juggler.jp/@SubwayTooter/(status_id) // https://mastodon.juggler.jp/@SubwayTooter/(status_id)
final String host = m.group( 1 ); final String host = m.group( 1 );
@ -957,38 +957,47 @@ public class ActMain extends AppCompatActivity
return; return;
} }
// ユーザページをアプリ内で開く
m = reUserPage.matcher( uri.toString() ); m = reUserPage.matcher( uri.toString() );
if( m.find() ){ if( m.find() ){
// ユーザページをアプリ内で開く
// https://mastodon.juggler.jp/@SubwayTooter // https://mastodon.juggler.jp/@SubwayTooter
final String host = m.group( 1 ); final String host = m.group( 1 );
final String user = Uri.decode( m.group( 2 ) ); final String user = Uri.decode( m.group( 2 ) );
ArrayList< SavedAccount > account_list_same_host = new ArrayList<>(); // 1: 検索APIは疑似アカウントでは開けない => startFindAccountが使えない
for( SavedAccount a : SavedAccount.loadAccountList( log ) ){ // 2: かりに検索できたとしてもユーザページは疑似アカウントでは開けない
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
ArrayList< SavedAccount > account_list_filtered = new ArrayList<>();
for( SavedAccount a : account_list ){
if( a.isPseudo() ) continue;
if( host.equalsIgnoreCase( a.host ) ){ if( host.equalsIgnoreCase( a.host ) ){
account_list_same_host.add( a ); account_list_filtered.add( a );
} }
} }
// ソートする if( account_list_filtered.isEmpty() ){
Collections.sort( account_list_same_host, new Comparator< SavedAccount >() {
@Override public int compare( SavedAccount a, SavedAccount b ){
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
}
} );
if( account_list_same_host.isEmpty() ){ for( SavedAccount a : account_list ){
account_list_same_host.add( addPseudoAccount( host ) ); if( a.isPseudo() ) continue;
account_list_filtered.add( a );
} }
AccountPicker.pick( this, true, true if( account_list_filtered.isEmpty() ){
// 認証されたアカウントが全くないのでブラウザで開くしかない
openChromeTab( getDefaultInsertPosition(), null, uri.toString(), true );
return;
}
}
AccountPicker.pick( this, false, true
, getString( R.string.account_picker_open_user_who, user + "@" + host ) , getString( R.string.account_picker_open_user_who, user + "@" + host )
, account_list_same_host , account_list_filtered
, new AccountPicker.AccountPickerCallback() { , new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull final SavedAccount ai ){ @Override public void onAccountPicked( @NonNull final SavedAccount ai ){
startGetAccount( ai, host, user, new GetAccountCallback() { startFindAccount( ai, host, user, new FindAccountCallback() {
@Override public void onGetAccount( TootAccount who ){ @Override public void onFindAccount( TootAccount who ){
if( who != null ){ if( who != null ){
performOpenUser( getDefaultInsertPosition(), ai, who ); performOpenUser( getDefaultInsertPosition(), ai, who );
return; return;
@ -1016,7 +1025,10 @@ public class ActMain extends AppCompatActivity
SavedAccount account = SavedAccount.loadAccount( log, db_id ); SavedAccount account = SavedAccount.loadAccount( log, db_id );
if( account != null ){ if( account != null ){
Column column = addColumn( getDefaultInsertPosition(), account, Column.TYPE_NOTIFICATIONS ); Column column = addColumn( getDefaultInsertPosition(), account, Column.TYPE_NOTIFICATIONS );
// 通知を読み直す
if( ! column.bInitialLoading ){
column.startLoading();
}
} }
}catch( Throwable ex ){ }catch( Throwable ex ){
ex.printStackTrace(); ex.printStackTrace();
@ -1024,6 +1036,8 @@ public class ActMain extends AppCompatActivity
return; return;
} }
// OAuth2 認証コールバック
final ProgressDialog progress = new ProgressDialog( ActMain.this ); final ProgressDialog progress = new ProgressDialog( ActMain.this );
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() { final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
@ -1228,7 +1242,7 @@ public class ActMain extends AppCompatActivity
int idx = page_delete - 1; int idx = page_delete - 1;
scrollToColumn( idx ); scrollToColumn( idx );
Column c = app_state.column_list.get( idx ); Column c = app_state.column_list.get( idx );
if( ! c.bInitialLoading ){ if( ! c.bFirstInitialized ){
c.startLoading(); c.startLoading();
} }
} }
@ -1242,7 +1256,7 @@ public class ActMain extends AppCompatActivity
int idx = page_delete - 1; int idx = page_delete - 1;
scrollToColumn( idx ); scrollToColumn( idx );
Column c = app_state.column_list.get( idx ); Column c = app_state.column_list.get( idx );
if( ! c.bInitialLoading ){ if( ! c.bFirstInitialized ){
c.startLoading(); c.startLoading();
} }
} }
@ -1268,7 +1282,7 @@ public class ActMain extends AppCompatActivity
Column col = new Column( app_state, ai, this, type, params ); Column col = new Column( app_state, ai, this, type, params );
index = addColumn( col, index ); index = addColumn( col, index );
scrollToColumn( index ); scrollToColumn( index );
if( ! col.bInitialLoading ){ if( ! col.bFirstInitialized ){
col.startLoading(); col.startLoading();
} }
return col; return col;
@ -1321,13 +1335,13 @@ public class ActMain extends AppCompatActivity
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
interface GetAccountCallback { interface FindAccountCallback {
// return account information // return account information
// if failed, account is null. // if failed, account is null.
void onGetAccount( TootAccount account ); void onFindAccount( TootAccount account );
} }
void startGetAccount( final SavedAccount access_info, final String host, final String user, final GetAccountCallback callback ){ void startFindAccount( final SavedAccount access_info, final String host, final String user, final FindAccountCallback callback ){
final ProgressDialog progress = new ProgressDialog( this ); final ProgressDialog progress = new ProgressDialog( this );
final AsyncTask< Void, Void, TootAccount > task = new AsyncTask< Void, Void, TootAccount >() { final AsyncTask< Void, Void, TootAccount > task = new AsyncTask< Void, Void, TootAccount >() {
@ -1380,7 +1394,7 @@ public class ActMain extends AppCompatActivity
@Override @Override
protected void onPostExecute( TootAccount result ){ protected void onPostExecute( TootAccount result ){
progress.dismiss(); progress.dismiss();
callback.onGetAccount( result ); callback.onFindAccount( result );
} }
}; };
@ -1396,15 +1410,15 @@ public class ActMain extends AppCompatActivity
task.executeOnExecutor( App1.task_executor ); task.executeOnExecutor( App1.task_executor );
} }
static final Pattern reHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)\\z" ); static final Pattern reHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)(?:\\z|\\?)" );
static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)\\z" ); static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)(?:\\z|\\?)" );
static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)/(\\d+)\\z" ); static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)/(\\d+)(?:\\z|\\?)" );
public void openChromeTab( final int pos, final SavedAccount access_info, final String url, boolean noIntercept ){ public void openChromeTab( final int pos, @Nullable final SavedAccount access_info, final String url, boolean noIntercept ){
try{ try{
log.d( "openChromeTab url=%s", url ); log.d( "openChromeTab url=%s", url );
if( ! noIntercept ){ if( ! noIntercept && access_info != null ){
// ハッシュタグをアプリ内で開く // ハッシュタグをアプリ内で開く
Matcher m = reHashTag.matcher( url ); Matcher m = reHashTag.matcher( url );
if( m.find() ){ if( m.find() ){
@ -1446,8 +1460,10 @@ public class ActMain extends AppCompatActivity
// https://mastodon.juggler.jp/@SubwayTooter // https://mastodon.juggler.jp/@SubwayTooter
final String host = m.group( 1 ); final String host = m.group( 1 );
final String user = Uri.decode( m.group( 2 ) ); final String user = Uri.decode( m.group( 2 ) );
startGetAccount( access_info, host, user, new GetAccountCallback() {
@Override public void onGetAccount( TootAccount who ){ if( ! access_info.isPseudo() ){
startFindAccount( access_info, host, user, new FindAccountCallback() {
@Override public void onFindAccount( TootAccount who ){
if( who != null ){ if( who != null ){
performOpenUser( pos, access_info, who ); performOpenUser( pos, access_info, who );
return; return;
@ -1457,7 +1473,51 @@ public class ActMain extends AppCompatActivity
} ); } );
return; return;
} }
// 1: 検索APIは疑似アカウントでは開けない => startFindAccountが使えない
// 2: かりに検索できたとしてもユーザページは疑似アカウントでは開けない
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
ArrayList< SavedAccount > account_list_filtered = new ArrayList<>();
for( SavedAccount a : account_list ){
if( a.isPseudo() ) continue;
if( host.equalsIgnoreCase( a.host ) ){
account_list_filtered.add( a );
}
}
if( account_list_filtered.isEmpty() ){
for( SavedAccount a : account_list ){
if( a.isPseudo() ) continue;
account_list_filtered.add( a );
}
if( account_list_filtered.isEmpty() ){
// 認証されたアカウントが全くないのでブラウザで開くしかない
openChromeTab( getDefaultInsertPosition(), null, url, true );
return;
}
}
AccountPicker.pick( this, false, true
, getString( R.string.account_picker_open_user_who, user + "@" + host )
, account_list_filtered
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull final SavedAccount ai ){
startFindAccount( ai, host, user, new FindAccountCallback() {
@Override public void onFindAccount( TootAccount who ){
if( who != null ){
performOpenUser( getDefaultInsertPosition(), ai, who );
return;
}
openChromeTab( getDefaultInsertPosition(), ai, url, true );
}
} );
}
} );
return;
}
} }
try{ try{
@ -1641,10 +1701,6 @@ public class ActMain extends AppCompatActivity
} ); } );
} }
public void performReply( SavedAccount account, TootStatus status ){
ActPost.open( this, REQUEST_CODE_POST, account.db_id, status );
}
public void performMention( SavedAccount account, TootAccount who ){ public void performMention( SavedAccount account, TootAccount who ){
ActPost.open( this, REQUEST_CODE_POST, account.db_id, "@" + account.getFullAcct( who ) + " " ); ActPost.open( this, REQUEST_CODE_POST, account.db_id, "@" + account.getFullAcct( who ) + " " );
} }
@ -1969,6 +2025,66 @@ public class ActMain extends AppCompatActivity
showColumnMatchAccount( access_info ); showColumnMatchAccount( access_info );
} }
public void performReply(
final SavedAccount access_info
, final TootStatus arg_status
, final boolean bRemote
){
if( ! bRemote ){
ActPost.open( this, REQUEST_CODE_POST, access_info.db_id, arg_status );
return;
}
new AsyncTask< Void, Void, TootApiResult >() {
TootStatus target_status;
@Override protected TootApiResult doInBackground( Void... params ){
TootApiClient client = new TootApiClient( ActMain.this, new TootApiClient.Callback() {
@Override public boolean isApiCancelled(){
return isCancelled();
}
@Override public void publishApiProgress( final String s ){
}
} );
client.setAccount( access_info );
// 検索APIに他タンスのステータスのURLを投げると自タンスのステータスを得られる
String path = String.format( Locale.JAPAN, Column.PATH_SEARCH, Uri.encode( arg_status.url ) );
path = path + "&resolve=1";
TootApiResult result = client.request( path );
if( result != null && result.object != null ){
TootResults tmp = TootResults.parse( log, access_info, result.object );
if( tmp != null && tmp.statuses != null && ! tmp.statuses.isEmpty() ){
target_status = tmp.statuses.get( 0 );
log.d( "status id conversion %s => %s", arg_status.id, target_status.id );
}
if( target_status == null ){
return new TootApiResult( getString( R.string.status_id_conversion_failed ) );
}
}
return result;
}
@Override
protected void onCancelled( TootApiResult result ){
super.onPostExecute( result );
}
@Override
protected void onPostExecute( TootApiResult result ){
if( result == null ){
// cancelled.
}else if( target_status != null ){
ActPost.open( ActMain.this, REQUEST_CODE_POST, access_info.db_id, target_status );
}else{
Utils.showToast( ActMain.this, true, result.error );
}
}
}.executeOnExecutor( App1.task_executor );
}
//////////////////////////////////////// ////////////////////////////////////////
private void performAccountSetting(){ private void performAccountSetting(){
@ -2838,6 +2954,21 @@ public class ActMain extends AppCompatActivity
} ); } );
} }
void openReplyFromAnotherAccount( @NonNull final SavedAccount access_info, final TootStatus status ){
if( status == null ) return;
AccountPicker.pick( this, false, false
, getString( R.string.account_picker_reply )
, makeAccountListNonPseudo( log ), new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
performReply(
ai
, status
, ! ai.host.equalsIgnoreCase( access_info.host )
);
}
} );
}
void openFollowFromAnotherAccount( @NonNull SavedAccount access_info, TootStatus status ){ void openFollowFromAnotherAccount( @NonNull SavedAccount access_info, TootStatus status ){
if( status == null ) return; if( status == null ) return;
openFollowFromAnotherAccount( access_info, status.account ); openFollowFromAnotherAccount( access_info, status.account );
@ -2855,6 +2986,9 @@ public class ActMain extends AppCompatActivity
} ); } );
} }
/////////////////////////////////////////////////////////////////////////
// タブレット対応で必要になった関数など
private boolean closeColumnSetting(){ private boolean closeColumnSetting(){
if( pager_adapter != null ){ if( pager_adapter != null ){
ColumnViewHolder vh = pager_adapter.getColumnViewHolder( pager.getCurrentItem() ); ColumnViewHolder vh = pager_adapter.getColumnViewHolder( pager.getCurrentItem() );
@ -2911,7 +3045,6 @@ public class ActMain extends AppCompatActivity
updateColumnStrip(); updateColumnStrip();
return index; return index;
} }
private void removeColumn( Column column ){ private void removeColumn( Column column ){

View File

@ -23,11 +23,12 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import jp.juggler.subwaytooter.api.TootApiClient; import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult; import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootApplication;
import jp.juggler.subwaytooter.api.entity.TootNotification; import jp.juggler.subwaytooter.api.entity.TootNotification;
import jp.juggler.subwaytooter.api.entity.TootStatus; import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.table.MutedApp; import jp.juggler.subwaytooter.table.MutedApp;
@ -146,44 +147,57 @@ public class AlarmService extends IntentService {
} }
} }
TootApiClient client = new TootApiClient( this, new TootApiClient.Callback() {
@Override public boolean isApiCancelled(){
return false;
}
@Override public void publishApiProgress( String s ){
} final AtomicBoolean bAlarmRequired = new AtomicBoolean( false );
} ); final HashSet< String > muted_app = MutedApp.getNameSet();
final HashSet< String > muted_word = MutedWord.getNameSet();
boolean bAlarmRequired = false; LinkedList<Thread> thread_list = new LinkedList<>( );
for( SavedAccount _a : account_list ){
final SavedAccount account = _a;
Thread t = new Thread( new Runnable() {
@Override public void run(){
HashSet< String > muted_app = MutedApp.getNameSet();
HashSet< String > muted_word = MutedWord.getNameSet();
for( SavedAccount account : account_list ){
try{ try{
if( account.notification_mention if( account.notification_mention
|| account.notification_boost || account.notification_boost
|| account.notification_favourite || account.notification_favourite
|| account.notification_follow || account.notification_follow
){ ){
bAlarmRequired = true; bAlarmRequired.set(true);
TootApiClient client = new TootApiClient( AlarmService.this, new TootApiClient.Callback() {
@Override public boolean isApiCancelled(){
return false;
}
@Override public void publishApiProgress( String s ){
}
} );
ArrayList< Data > data_list = new ArrayList<>(); ArrayList< Data > data_list = new ArrayList<>();
checkAccount( client, data_list, account, muted_app ,muted_word); checkAccount( client, data_list, account, muted_app ,muted_word);
showNotification( account.db_id, data_list ); showNotification( account.db_id, data_list );
} }
}catch( Throwable ex ){ }catch( Throwable ex ){
ex.printStackTrace(); ex.printStackTrace();
} }
} }
} );
thread_list.add( t);
t.start();
}
for( Thread t : thread_list ){
try{
t.join();
}catch(Throwable ex){
ex.printStackTrace( );
}
}
alarm_manager.cancel( pi_next ); alarm_manager.cancel( pi_next );
if( bAlarmRequired ){ if( bAlarmRequired.get() ){
long now = SystemClock.elapsedRealtime(); long now = SystemClock.elapsedRealtime();
alarm_manager.setWindow( alarm_manager.setWindow(
AlarmManager.ELAPSED_REALTIME_WAKEUP AlarmManager.ELAPSED_REALTIME_WAKEUP

View File

@ -48,7 +48,6 @@ import jp.juggler.subwaytooter.util.Utils;
class Column implements StreamReader.Callback { class Column implements StreamReader.Callback {
private static final LogCategory log = new LogCategory( "Column" ); private static final LogCategory log = new LogCategory( "Column" );
interface Callback { interface Callback {
boolean isActivityResume(); boolean isActivityResume();
} }
@ -374,6 +373,13 @@ class Column implements StreamReader.Callback {
void dispose(){ void dispose(){
is_dispose.set( true ); is_dispose.set( true );
stopStreaming(); stopStreaming();
if( _holder != null ){
try{
_holder.getListView().setAdapter( null );
}catch( Throwable ignored ){
}
}
} }
String getColumnName( boolean bLong ){ String getColumnName( boolean bLong ){
@ -698,41 +704,43 @@ class Column implements StreamReader.Callback {
AlarmService.dataRemoved( context, access_info.db_id ); AlarmService.dataRemoved( context, access_info.db_id );
} }
private ColumnViewHolder holder; //////////////////////////////////////////////////////////////////////////////////////
void setColumnViewHolder( ColumnViewHolder holder ){ private ColumnViewHolder _holder;
this.holder = holder;
void setColumnViewHolder( ColumnViewHolder new_holder ){
this._holder = new_holder;
} }
private final Runnable proc_showContent = new Runnable() { private ColumnViewHolder getViewHolder(){
@Override public void run(){ return is_dispose.get() ? null : _holder;
if( holder != null ) holder.showContent();
} }
};
private final Runnable proc_showColumnHeader = new Runnable() {
@Override public void run(){
if( holder != null ) holder.showColumnHeader();
}
};
private final Runnable proc_showColumnColor = new Runnable() {
@Override public void run(){
if( holder != null ) holder.showColumnColor();
}
};
void fireShowContent(){ void fireShowContent(){
Utils.runOnMainThread( proc_showContent ); if( ! Utils.isMainThread() ){
throw new RuntimeException( "fireShowColumnHeader: not on main thread." );
}
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.showContent();
} }
// カラムヘッダ部分だけ更新する
void fireShowColumnHeader(){ void fireShowColumnHeader(){
Utils.runOnMainThread( proc_showColumnHeader ); if( ! Utils.isMainThread() ){
throw new RuntimeException( "fireShowColumnHeader: not on main thread." );
}
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.showColumnHeader();
} }
void fireColumnColor(){ void fireColumnColor(){
Utils.runOnMainThread( proc_showColumnColor ); if( ! Utils.isMainThread() ){
throw new RuntimeException( "fireShowColumnHeader: not on main thread." );
}
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.showColumnColor();
} }
//////////////////////////////////////////////////////////////////////////////////////
private AsyncTask< Void, Void, TootApiResult > last_task; private AsyncTask< Void, Void, TootApiResult > last_task;
@ -907,6 +915,13 @@ class Column implements StreamReader.Callback {
fireShowContent(); fireShowContent();
try{
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.getRefreshLayout().setRefreshing( false );
}catch(Throwable ignored){
}
AsyncTask< Void, Void, TootApiResult > task = this.last_task = new AsyncTask< Void, Void, TootApiResult >() { AsyncTask< Void, Void, TootApiResult > task = this.last_task = new AsyncTask< Void, Void, TootApiResult >() {
TootApiResult parseAccount1( TootApiClient client, String path_base ){ TootApiResult parseAccount1( TootApiClient client, String path_base ){
@ -1166,6 +1181,7 @@ class Column implements StreamReader.Callback {
@Override @Override
protected void onPostExecute( TootApiResult result ){ protected void onPostExecute( TootApiResult result ){
if( is_dispose.get() ) return;
if( isCancelled() || result == null ){ if( isCancelled() || result == null ){
return; return;
@ -1189,7 +1205,8 @@ class Column implements StreamReader.Callback {
// 初期ロードの直後は先頭に移動する // 初期ロードの直後は先頭に移動する
try{ try{
holder.getListView().setSelection( 0 ); ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.getListView().setSelection( 0 );
}catch( Throwable ignored ){ }catch( Throwable ignored ){
} }
} }
@ -1362,14 +1379,15 @@ class Column implements StreamReader.Callback {
case TYPE_FEDERATE: case TYPE_FEDERATE:
startRefresh( true, false, status_id, refresh_after_toot ); startRefresh( true, false, status_id, refresh_after_toot );
break; break;
case TYPE_PROFILE: case TYPE_PROFILE:
if( profile_tab == TAB_STATUS && profile_id == access_info.id ){ if( profile_tab == TAB_STATUS && profile_id == access_info.id ){
startRefresh( true, false, status_id, refresh_after_toot ); startRefresh( true, false, status_id, refresh_after_toot );
} }
break; break;
case TYPE_CONVERSATION: case TYPE_CONVERSATION:
startLoading(); startLoading();
break; break;
} }
} }
@ -1381,22 +1399,26 @@ class Column implements StreamReader.Callback {
if( last_task != null ){ if( last_task != null ){
if( ! bSilent ){ if( ! bSilent ){
Utils.showToast( context, true, R.string.column_is_busy ); Utils.showToast( context, true, R.string.column_is_busy );
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.getRefreshLayout().setRefreshing( false ); if( holder != null ) holder.getRefreshLayout().setRefreshing( false );
} }
return; return;
}else if( bBottom && max_id == null ){ }else if( bBottom && max_id == null ){
if( ! bSilent ){ if( ! bSilent ){
Utils.showToast( context, true, R.string.end_of_list ); Utils.showToast( context, true, R.string.end_of_list );
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.getRefreshLayout().setRefreshing( false ); if( holder != null ) holder.getRefreshLayout().setRefreshing( false );
} }
return; return;
}else if( ! bBottom && since_id == null ){ }else if( ! bBottom && since_id == null ){
ColumnViewHolder holder = getViewHolder();
if( holder != null ) holder.getRefreshLayout().setRefreshing( false ); if( holder != null ) holder.getRefreshLayout().setRefreshing( false );
startLoading(); startLoading();
return; return;
} }
if( bSilent ){ if( bSilent ){
ColumnViewHolder holder = getViewHolder();
if( holder != null ){ if( holder != null ){
holder.getRefreshLayout().setRefreshing( true ); holder.getRefreshLayout().setRefreshing( true );
} }
@ -1820,6 +1842,8 @@ class Column implements StreamReader.Callback {
@Override @Override
protected void onPostExecute( TootApiResult result ){ protected void onPostExecute( TootApiResult result ){
if( is_dispose.get() ) return;
if( isCancelled() || result == null ){ if( isCancelled() || result == null ){
return; return;
} }
@ -1846,6 +1870,7 @@ class Column implements StreamReader.Callback {
// 事前にスクロール位置を覚えておく // 事前にスクロール位置を覚えておく
ScrollPosition sp = null; ScrollPosition sp = null;
ColumnViewHolder holder = getViewHolder();
if( holder != null ){ if( holder != null ){
sp = holder.getScrollPosition(); sp = holder.getScrollPosition();
} }
@ -1911,6 +1936,7 @@ class Column implements StreamReader.Callback {
return; return;
} }
ColumnViewHolder holder = getViewHolder();
if( holder != null ){ if( holder != null ){
holder.getRefreshLayout().setRefreshing( true ); holder.getRefreshLayout().setRefreshing( true );
} }
@ -2223,10 +2249,12 @@ class Column implements StreamReader.Callback {
@Override @Override
protected void onPostExecute( TootApiResult result ){ protected void onPostExecute( TootApiResult result ){
if( is_dispose.get() ) return;
if( isCancelled() || result == null ){ if( isCancelled() || result == null ){
return; return;
} }
last_task = null; last_task = null;
bRefreshLoading = false; bRefreshLoading = false;
@ -2243,16 +2271,18 @@ class Column implements StreamReader.Callback {
ArrayList< Object > list_new = duplicate_map.filterDuplicate( list_tmp ); ArrayList< Object > list_new = duplicate_map.filterDuplicate( list_tmp );
ColumnViewHolder holder = getViewHolder();
// idx番目の要素がListViewのtopから何ピクセル下にあるか // idx番目の要素がListViewのtopから何ピクセル下にあるか
int restore_idx = position + 1; int restore_idx = position + 1;
int restore_y = 0; int restore_y = 0;
if( holder != null ){ if( holder != null ){
try{ try{
restore_y = getItemTop( restore_idx ); restore_y = getItemTop( holder, restore_idx );
}catch( IndexOutOfBoundsException ex ){ }catch( IndexOutOfBoundsException ex ){
restore_idx = position; restore_idx = position;
try{ try{
restore_y = getItemTop( restore_idx ); restore_y = getItemTop( holder, restore_idx );
}catch( IndexOutOfBoundsException ex2 ){ }catch( IndexOutOfBoundsException ex2 ){
restore_idx = - 1; restore_idx = - 1;
} }
@ -2267,7 +2297,7 @@ class Column implements StreamReader.Callback {
if( holder != null ){ if( holder != null ){
//noinspection StatementWithEmptyBody //noinspection StatementWithEmptyBody
if( restore_idx >= 0 ){ if( restore_idx >= 0 ){
setItemTop( restore_idx + added - 1, restore_y ); setItemTop( holder, restore_idx + added - 1, restore_y );
}else{ }else{
// ギャップが画面内にない場合何もしない // ギャップが画面内にない場合何もしない
} }
@ -2293,7 +2323,8 @@ class Column implements StreamReader.Callback {
} }
// 特定の要素が特定の位置に来るようにスクロール位置を調整する // 特定の要素が特定の位置に来るようにスクロール位置を調整する
private void setItemTop( int idx, int y ){ private void setItemTop( @NonNull ColumnViewHolder holder, int idx, int y ){
MyListView listView = holder.getListView(); MyListView listView = holder.getListView();
boolean hasHeader = holder.hasHeaderView(); boolean hasHeader = holder.hasHeaderView();
if( hasHeader ){ if( hasHeader ){
@ -2309,7 +2340,8 @@ class Column implements StreamReader.Callback {
listView.setSelectionFromTop( idx, y ); listView.setSelectionFromTop( idx, y );
} }
private int getItemTop( int idx ){ private int getItemTop( @NonNull ColumnViewHolder holder, int idx ){
MyListView listView = holder.getListView(); MyListView listView = holder.getListView();
boolean hasHeader = holder.hasHeaderView(); boolean hasHeader = holder.hasHeaderView();
@ -2382,6 +2414,7 @@ class Column implements StreamReader.Callback {
log.e( ex, "getId() failed. o=", list_new.get( 0 ) ); log.e( ex, "getId() failed. o=", list_new.get( 0 ) );
} }
} }
ColumnViewHolder holder = getViewHolder();
// 事前にスクロール位置を覚えておく // 事前にスクロール位置を覚えておく
ScrollPosition sp = null; ScrollPosition sp = null;
@ -2396,7 +2429,7 @@ class Column implements StreamReader.Callback {
if( list_data.size() > 0 ){ if( list_data.size() > 0 ){
try{ try{
restore_idx = holder.getListView().getFirstVisiblePosition(); restore_idx = holder.getListView().getFirstVisiblePosition();
restore_y = getItemTop( restore_idx ); restore_y = getItemTop( holder, restore_idx );
}catch( IndexOutOfBoundsException ex ){ }catch( IndexOutOfBoundsException ex ){
restore_idx = - 1; restore_idx = - 1;
restore_y = 0; restore_y = 0;
@ -2426,11 +2459,11 @@ class Column implements StreamReader.Callback {
if( holder != null ){ if( holder != null ){
//noinspection StatementWithEmptyBody //noinspection StatementWithEmptyBody
if( sp == null || ( sp.pos == 0 && sp.top == 0 ) ){ if( sp.pos == 0 && sp.top == 0 ){
// スクロール位置が先頭なら先頭のまま // スクロール位置が先頭なら先頭のまま
}else if( restore_idx >= 0 ){ }else if( restore_idx >= 0 ){
// //
setItemTop( restore_idx + added, restore_y ); setItemTop( holder, restore_idx + added, restore_y );
}else{ }else{
// ギャップが画面内にない場合何もしない // ギャップが画面内にない場合何もしない
} }
@ -2446,6 +2479,7 @@ class Column implements StreamReader.Callback {
}; };
@Override public void onStreamingMessage( String event_type, Object o ){ @Override public void onStreamingMessage( String event_type, Object o ){
if( is_dispose.get() ) return;
if( o instanceof Long ){ if( o instanceof Long ){
removeStatus( access_info, (Long) o ); removeStatus( access_info, (Long) o );
@ -2589,7 +2623,6 @@ class Column implements StreamReader.Callback {
private boolean bPutGap; private boolean bPutGap;
private void resumeStreaming( boolean bPutGap ){ private void resumeStreaming( boolean bPutGap ){
if( ! canStreaming() ){ if( ! canStreaming() ){

View File

@ -14,7 +14,7 @@ class ColumnPagerAdapter extends PagerAdapter {
private final ActMain activity; private final ActMain activity;
private final LayoutInflater inflater; private final LayoutInflater inflater;
final ArrayList< Column > column_list; private final ArrayList< Column > column_list;
private final SparseArray< ColumnViewHolder > holder_list = new SparseArray<>(); private final SparseArray< ColumnViewHolder > holder_list = new SparseArray<>();
@ -51,11 +51,11 @@ class ColumnPagerAdapter extends PagerAdapter {
container.addView( root, 0 ); container.addView( root, 0 );
Column column = column_list.get( page_idx ); Column column = column_list.get( page_idx );
ColumnViewHolder holder = new ColumnViewHolder( activity, column ); ColumnViewHolder holder = new ColumnViewHolder( activity,root );
// //
holder_list.put( page_idx, holder ); holder_list.put( page_idx, holder );
// //
holder.onPageCreate( root, page_idx, column_list.size() ); holder.onPageCreate( column, page_idx, column_list.size() );
return root; return root;
} }
@ -68,8 +68,7 @@ class ColumnPagerAdapter extends PagerAdapter {
ColumnViewHolder holder = holder_list.get( page_idx ); ColumnViewHolder holder = holder_list.get( page_idx );
holder_list.remove( page_idx ); holder_list.remove( page_idx );
if( holder != null ){ if( holder != null ){
holder.onPageDestroy();
holder.onPageDestroy( view );
} }
} }
} }

View File

@ -3,6 +3,7 @@ package jp.juggler.subwaytooter;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
@ -16,12 +17,12 @@ import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView; import android.widget.TextView;
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout; import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout;
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection; import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jp.juggler.subwaytooter.table.AcctColor; import jp.juggler.subwaytooter.table.AcctColor;
@ -38,66 +39,54 @@ class ColumnViewHolder
private static final LogCategory log = new LogCategory( "ColumnViewHolder" ); private static final LogCategory log = new LogCategory( "ColumnViewHolder" );
final ActMain activity; final ActMain activity;
final Column column;
private final AtomicBoolean is_destroyed = new AtomicBoolean( false );
private final ItemListAdapter status_adapter;
ColumnViewHolder( ActMain activity, Column column ){ @Nullable Column column;
this.activity = activity; @Nullable private HeaderViewHolder vh_header;
this.column = column; @Nullable private ItemListAdapter status_adapter;
this.status_adapter = new ItemListAdapter( activity, column );
}
private boolean isPageDestroyed(){ private final TextView tvLoading;
return is_destroyed.get() || activity.isFinishing(); private final MyListView listView;
} private final SwipyRefreshLayout swipyRefreshLayout;
void onPageDestroy( @SuppressWarnings("UnusedParameters") View root ){ private final View llColumnHeader;
log.d( "onPageDestroy:%s", column.getColumnName( true ) ); private final TextView tvColumnIndex;
is_destroyed.set( true ); private final TextView tvColumnContext;
private final ImageView ivColumnIcon;
private final TextView tvColumnName;
saveScrollPosition(); private final View llColumnSetting;
column.setColumnViewHolder( null ); private final View btnSearch;
private final EditText etSearch;
private final CheckBox cbResolve;
private final EditText etRegexFilter;
private final TextView tvRegexFilterError;
closeBitmaps(); private final ImageButton btnColumnSetting;
private final ImageButton btnColumnReload;
private final ImageButton btnColumnClose;
activity.closeListItemPopup(); private final View flColumnBackground;
private final ImageView ivColumnBackgroundImage;
private final View llSearch;
private final CheckBox cbDontCloseColumn;
private final CheckBox cbWithAttachment;
private final CheckBox cbDontShowBoost;
private final CheckBox cbDontShowReply;
private final CheckBox cbDontStreaming;
private final CheckBox cbDontAutoRefresh;
private final CheckBox cbHideMediaDefault;
private final View llRegexFilter;
private final Button btnDeleteNotification;
} ColumnViewHolder( ActMain arg_activity, View root ){
this.activity = arg_activity;
private TextView tvLoading;
private MyListView listView;
private TextView tvColumnContext;
private TextView tvColumnName;
private HeaderViewHolder vh_header;
private SwipyRefreshLayout swipyRefreshLayout;
private View btnSearch;
private EditText etSearch;
private CheckBox cbResolve;
private View llColumnSetting;
private EditText etRegexFilter;
private TextView tvRegexFilterError;
private ImageView ivColumnIcon;
private View llColumnHeader;
private TextView tvColumnIndex;
private ImageButton btnColumnSetting;
private ImageButton btnColumnReload;
private ImageButton btnColumnClose;
private View flColumnBackground;
private ImageView ivColumnBackgroundImage;
void onPageCreate( View root, int page_idx, int page_count ){
log.d( "onPageCreate:%s", column.getColumnName( true ) );
flColumnBackground = root.findViewById( R.id.flColumnBackground ); flColumnBackground = root.findViewById( R.id.flColumnBackground );
ivColumnBackgroundImage = (ImageView) root.findViewById( R.id.ivColumnBackgroundImage ); ivColumnBackgroundImage = (ImageView) root.findViewById( R.id.ivColumnBackgroundImage );
llColumnHeader = root.findViewById( R.id.llColumnHeader ); llColumnHeader = root.findViewById( R.id.llColumnHeader );
tvColumnIndex = (TextView) root.findViewById( R.id.tvColumnIndex ); tvColumnIndex = (TextView) root.findViewById( R.id.tvColumnIndex );
tvColumnIndex.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
tvColumnName = (TextView) root.findViewById( R.id.tvColumnName ); tvColumnName = (TextView) root.findViewById( R.id.tvColumnName );
tvColumnContext = (TextView) root.findViewById( R.id.tvColumnContext ); tvColumnContext = (TextView) root.findViewById( R.id.tvColumnContext );
@ -107,35 +96,129 @@ class ColumnViewHolder
btnColumnReload = (ImageButton) root.findViewById( R.id.btnColumnReload ); btnColumnReload = (ImageButton) root.findViewById( R.id.btnColumnReload );
btnColumnClose = (ImageButton) root.findViewById( R.id.btnColumnClose ); btnColumnClose = (ImageButton) root.findViewById( R.id.btnColumnClose );
tvLoading = (TextView) root.findViewById( R.id.tvLoading );
listView = (MyListView) root.findViewById( R.id.listView );
btnSearch = root.findViewById( R.id.btnSearch );
etSearch = (EditText) root.findViewById( R.id.etSearch );
cbResolve = (CheckBox) root.findViewById( R.id.cbResolve );
llSearch = root.findViewById( R.id.llSearch );
llColumnSetting = root.findViewById( R.id.llColumnSetting );
cbDontCloseColumn = (CheckBox) root.findViewById( R.id.cbDontCloseColumn );
cbWithAttachment = (CheckBox) root.findViewById( R.id.cbWithAttachment );
cbDontShowBoost = (CheckBox) root.findViewById( R.id.cbDontShowBoost );
cbDontShowReply = (CheckBox) root.findViewById( R.id.cbDontShowReply );
cbDontStreaming = (CheckBox) root.findViewById( R.id.cbDontStreaming );
cbDontAutoRefresh = (CheckBox) root.findViewById( R.id.cbDontAutoRefresh );
cbHideMediaDefault = (CheckBox) root.findViewById( R.id.cbHideMediaDefault );
etRegexFilter = (EditText) root.findViewById( R.id.etRegexFilter );
llRegexFilter = root.findViewById( R.id.llRegexFilter );
tvRegexFilterError = (TextView) root.findViewById( R.id.tvRegexFilterError );
listView.setOnItemClickListener( status_adapter );
btnDeleteNotification = (Button) root.findViewById( R.id.btnDeleteNotification );
llColumnHeader.setOnClickListener( this );
btnColumnSetting.setOnClickListener( this ); btnColumnSetting.setOnClickListener( this );
btnColumnReload.setOnClickListener( this ); btnColumnReload.setOnClickListener( this );
btnColumnClose.setOnClickListener( this ); btnColumnClose.setOnClickListener( this );
btnDeleteNotification.setOnClickListener( this );
llColumnHeader.setOnClickListener( this );
root.findViewById( R.id.btnColor ).setOnClickListener( this ); root.findViewById( R.id.btnColor ).setOnClickListener( this );
tvLoading = (TextView) root.findViewById( R.id.tvLoading ); this.swipyRefreshLayout = (SwipyRefreshLayout) root.findViewById( R.id.swipyRefreshLayout );
listView = (MyListView) root.findViewById( R.id.listView ); swipyRefreshLayout.setOnRefreshListener( this );
swipyRefreshLayout.setDistanceToTriggerSync( (int) ( 0.5f + 20f * activity.density ) );
cbDontCloseColumn.setOnCheckedChangeListener( this );
cbWithAttachment.setOnCheckedChangeListener( this );
cbDontShowBoost.setOnCheckedChangeListener( this );
cbDontShowReply.setOnCheckedChangeListener( this );
cbDontStreaming.setOnCheckedChangeListener( this );
cbDontAutoRefresh.setOnCheckedChangeListener( this );
cbHideMediaDefault.setOnCheckedChangeListener( this );
// 入力の追跡
etRegexFilter.addTextChangedListener( new TextWatcher() {
@Override
public void beforeTextChanged( CharSequence s, int start, int count, int after ){
}
@Override
public void onTextChanged( CharSequence s, int start, int before, int count ){
}
@Override public void afterTextChanged( Editable s ){
if( loading_busy ) return;
activity.handler.removeCallbacks( proc_start_filter );
if( isRegexValid() ){
activity.handler.postDelayed( proc_start_filter, 1500L );
}
}
} );
btnSearch.setOnClickListener( this );
etSearch.setOnEditorActionListener( new TextView.OnEditorActionListener() {
@Override public boolean onEditorAction( TextView v, int actionId, KeyEvent event ){
if( !loading_busy ){
if( actionId == EditorInfo.IME_ACTION_SEARCH ){
btnSearch.performClick();
return true;
}
}
return false;
}
} );
}
private boolean isPageDestroyed(){
return column ==null || activity.isFinishing();
}
void onPageDestroy(){
// タブレットモードの場合onPageCreateより前に呼ばれる
if( column != null ){
log.d( "onPageDestroy #%s", tvColumnName.getText() );
saveScrollPosition();
listView.setAdapter( null ); listView.setAdapter( null );
column.setColumnViewHolder( null );
column = null;
}
closeBitmaps();
activity.closeListItemPopup();
}
private static void vg( View v, boolean visible ){
v.setVisibility( visible ? View.VISIBLE : View.GONE );
}
private boolean loading_busy;
void onPageCreate( Column column, int page_idx, int page_count ){
loading_busy = true;
try{
this.column = column;
log.d( "onPageCreate:%s", column.getColumnName( true ) );
tvColumnIndex.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
listView.setAdapter( null );
this.status_adapter = new ItemListAdapter( activity, column );
if( column.column_type == Column.TYPE_PROFILE ){ if( column.column_type == Column.TYPE_PROFILE ){
vh_header = new HeaderViewHolder( activity, column, listView ); vh_header = new HeaderViewHolder( activity, column, listView );
status_adapter.header = vh_header; status_adapter.header = vh_header;
}else{ }else{
status_adapter.header = null; status_adapter.header = null;
} }
listView.setAdapter( status_adapter );
this.swipyRefreshLayout = (SwipyRefreshLayout) root.findViewById( R.id.swipyRefreshLayout );
swipyRefreshLayout.setOnRefreshListener( this );
swipyRefreshLayout.setDistanceToTriggerSync( (int) ( 0.5f + 20f * activity.density ) );
View llSearch = root.findViewById( R.id.llSearch );
btnSearch = root.findViewById( R.id.btnSearch );
etSearch = (EditText) root.findViewById( R.id.etSearch );
cbResolve = (CheckBox) root.findViewById( R.id.cbResolve );
listView.setFastScrollEnabled( ! Pref.pref( activity ).getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, true ) );
boolean bAllowFilter; boolean bAllowFilter;
switch( column.column_type ){ switch( column.column_type ){
@ -164,115 +247,37 @@ class ColumnViewHolder
break; break;
} }
llColumnSetting = root.findViewById( R.id.llColumnSetting );
llColumnSetting.setVisibility( View.GONE ); llColumnSetting.setVisibility( View.GONE );
CheckBox cb; cbDontCloseColumn.setChecked( column.dont_close );
cb = (CheckBox) root.findViewById( R.id.cbDontCloseColumn ); cbWithAttachment.setChecked( column.with_attachment );
cb.setChecked( column.dont_close ); cbDontShowBoost.setChecked( column.dont_show_boost );
cb.setOnCheckedChangeListener( this ); cbDontShowReply.setChecked( column.dont_show_reply );
cbDontStreaming.setChecked( column.dont_streaming );
cbDontAutoRefresh.setChecked( column.dont_auto_refresh );
cbHideMediaDefault.setChecked( column.hide_media_default );
cb = (CheckBox) root.findViewById( R.id.cbWithAttachment );
cb.setChecked( column.with_attachment );
cb.setOnCheckedChangeListener( this );
cb.setEnabled( bAllowFilter );
cb.setVisibility( bAllowFilter ? View.VISIBLE : View.GONE );
cb = (CheckBox) root.findViewById( R.id.cbDontShowBoost );
cb.setChecked( column.dont_show_boost );
cb.setOnCheckedChangeListener( this );
cb.setEnabled( bAllowFilter );
cb.setVisibility( bAllowFilterBoost ? View.VISIBLE : View.GONE );
cb = (CheckBox) root.findViewById( R.id.cbDontShowReply );
cb.setChecked( column.dont_show_reply );
cb.setOnCheckedChangeListener( this );
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 );
}
cb = (CheckBox) root.findViewById( R.id.cbHideMediaDefault );
if( ! column.canShowMedia() ){
cb.setVisibility( View.GONE );
}else{
cb.setVisibility(View.VISIBLE );
cb.setChecked( column.hide_media_default );
cb.setOnCheckedChangeListener( this );
}
etRegexFilter = (EditText) root.findViewById( R.id.etRegexFilter );
if( ! bAllowFilter ){
etRegexFilter.setVisibility( View.GONE );
root.findViewById( R.id.llRegexFilter ).setVisibility( View.GONE );
}else{
etRegexFilter.setText( column.regex_text ); etRegexFilter.setText( column.regex_text );
// tvRegexFilterErrorの表示を更新
tvRegexFilterError = (TextView) root.findViewById( R.id.tvRegexFilterError );
isRegexValid();
// 入力の追跡
etRegexFilter.addTextChangedListener( new TextWatcher() {
@Override
public void beforeTextChanged( CharSequence s, int start, int count, int after ){
}
@Override
public void onTextChanged( CharSequence s, int start, int before, int count ){
}
@Override public void afterTextChanged( Editable s ){
activity.handler.removeCallbacks( proc_start_filter );
if( isRegexValid() ){
activity.handler.postDelayed( proc_start_filter, 1500L );
}
}
} );
}
Button button = (Button) root.findViewById( R.id.btnDeleteNotification );
if( column.column_type != Column.TYPE_NOTIFICATIONS ){
button.setVisibility( View.GONE );
}else{
button.setVisibility( View.VISIBLE );
button.setOnClickListener( this );
}
if( column.column_type != Column.TYPE_SEARCH ){
llSearch.setVisibility( View.GONE );
}else{
llSearch.setVisibility( View.VISIBLE );
etSearch.setText( column.search_query ); etSearch.setText( column.search_query );
cbResolve.setChecked( column.search_resolve ); cbResolve.setChecked( column.search_resolve );
btnSearch.setOnClickListener( this );
etSearch.setOnEditorActionListener( new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction( TextView v, int actionId, KeyEvent event ){
if( actionId == EditorInfo.IME_ACTION_SEARCH ){
btnSearch.performClick();
return true;
}
return false;
}
} );
}
vg( cbWithAttachment, bAllowFilter );
vg( cbDontShowBoost, bAllowFilterBoost );
vg( cbDontShowReply, bAllowFilterBoost );
vg( cbDontStreaming, column.canStreaming() );
vg( cbDontAutoRefresh, column.canAutoRefresh() );
vg( cbHideMediaDefault, column.canShowMedia() );
vg( etRegexFilter, bAllowFilter );
vg( llRegexFilter, bAllowFilter );
vg( btnDeleteNotification, column.column_type == Column.TYPE_NOTIFICATIONS );
vg( llSearch, column.column_type == Column.TYPE_SEARCH );
// tvRegexFilterErrorの表示を更新
if( bAllowFilter ){
isRegexValid();
}
switch( column.column_type ){ switch( column.column_type ){
case Column.TYPE_CONVERSATION: case Column.TYPE_CONVERSATION:
@ -284,22 +289,23 @@ class ColumnViewHolder
break; break;
} }
if( column.bSimpleList ){
listView.setOnItemClickListener( status_adapter );
}else{
listView.setOnItemClickListener( null );
}
// //
listView.setAdapter( status_adapter );
listView.setFastScrollEnabled( ! Pref.pref( activity ).getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, true ) );
column.setColumnViewHolder( this ); column.setColumnViewHolder( this );
showColumnColor(); showColumnColor();
showContent(); showContent();
}finally{
loading_busy = false;
}
} }
void showColumnColor(){ void showColumnColor(){
if( column == null ) return;
int c = column.header_bg_color; int c = column.header_bg_color;
if( c == 0 ){ if( c == 0 ){
llColumnHeader.setBackgroundResource( R.drawable.btn_bg_ddd ); llColumnHeader.setBackgroundResource( R.drawable.btn_bg_ddd );
@ -393,6 +399,8 @@ class ColumnViewHolder
private final Runnable proc_start_filter = new Runnable() { private final Runnable proc_start_filter = new Runnable() {
@Override public void run(){ @Override public void run(){
if( isPageDestroyed() ) return; if( isPageDestroyed() ) return;
if( column == null ) return;
if( isRegexValid() ){ if( isRegexValid() ){
column.regex_text = etRegexFilter.getText().toString(); column.regex_text = etRegexFilter.getText().toString();
activity.app_state.saveColumnList(); activity.app_state.saveColumnList();
@ -430,10 +438,13 @@ class ColumnViewHolder
} }
@Override public void onRefresh( SwipyRefreshLayoutDirection direction ){ @Override public void onRefresh( SwipyRefreshLayoutDirection direction ){
if( column == null ) return;
column.startRefresh( false, direction == SwipyRefreshLayoutDirection.BOTTOM, - 1L, - 1 ); column.startRefresh( false, direction == SwipyRefreshLayoutDirection.BOTTOM, - 1L, - 1 );
} }
@Override public void onCheckedChanged( CompoundButton view, boolean isChecked ){ @Override public void onCheckedChanged( CompoundButton view, boolean isChecked ){
if( loading_busy || column ==null || status_adapter ==null ) return;
switch( view.getId() ){ switch( view.getId() ){
case R.id.cbDontCloseColumn: case R.id.cbDontCloseColumn:
@ -454,7 +465,6 @@ class ColumnViewHolder
column.startLoading(); column.startLoading();
break; break;
case R.id.cbDontShowReply: case R.id.cbDontShowReply:
column.dont_show_reply = isChecked; column.dont_show_reply = isChecked;
activity.app_state.saveColumnList(); activity.app_state.saveColumnList();
@ -487,6 +497,8 @@ class ColumnViewHolder
@Override @Override
public void onClick( View v ){ public void onClick( View v ){
if( loading_busy || column ==null || status_adapter ==null ) return;
switch( v.getId() ){ switch( v.getId() ){
case R.id.btnColumnClose: case R.id.btnColumnClose:
activity.closeColumn( false, column ); activity.closeColumn( false, column );
@ -535,13 +547,10 @@ class ColumnViewHolder
swipyRefreshLayout.setVisibility( View.GONE ); swipyRefreshLayout.setVisibility( View.GONE );
// ロード完了後に先頭から表示させる
if( status_adapter.getCount() > 0 ){
listView.setSelectionFromTop( 0, 0 );
}
} }
private void showColumnCloseButton(){ private void showColumnCloseButton(){
if( column == null ) return;
// カラム保護の状態 // カラム保護の状態
btnColumnClose.setEnabled( ! column.dont_close ); btnColumnClose.setEnabled( ! column.dont_close );
btnColumnClose.setAlpha( column.dont_close ? 0.3f : 1f ); btnColumnClose.setAlpha( column.dont_close ? 0.3f : 1f );
@ -564,6 +573,7 @@ class ColumnViewHolder
// カラムヘッダなど負荷が低い部分の表示更新 // カラムヘッダなど負荷が低い部分の表示更新
void showColumnHeader(){ void showColumnHeader(){
if( column == null ) return;
String acct = column.access_info.acct; String acct = column.access_info.acct;
AcctColor ac = AcctColor.load( acct ); AcctColor ac = AcctColor.load( acct );
@ -592,14 +602,14 @@ class ColumnViewHolder
// クラッシュレポートにadapterとリストデータの状態不整合が多かったので // クラッシュレポートにadapterとリストデータの状態不整合が多かったので
// とりあえずリストデータ変更の通知だけは最優先で行っておく // とりあえずリストデータ変更の通知だけは最優先で行っておく
try{ try{
status_adapter.notifyDataSetChanged(); if( status_adapter != null ) status_adapter.notifyDataSetChanged();
}catch( Throwable ex ){ }catch( Throwable ex ){
ex.printStackTrace(); ex.printStackTrace();
} }
showColumnHeader(); showColumnHeader();
if( column.is_dispose.get() ){ if( column == null || column.is_dispose.get() ){
showError( "column was disposed." ); showError( "column was disposed." );
return; return;
} }
@ -645,8 +655,11 @@ class ColumnViewHolder
} }
private void restoreScrollPosition(){ private void restoreScrollPosition(){
if( column == null ) return;
ScrollPosition sp = column.scroll_save; ScrollPosition sp = column.scroll_save;
if( sp == null ) return; if( sp == null ) return;
column.scroll_save = null; column.scroll_save = null;
if( listView.getVisibility() == View.VISIBLE ){ if( listView.getVisibility() == View.VISIBLE ){
@ -656,27 +669,30 @@ class ColumnViewHolder
} }
private void saveScrollPosition(){ private void saveScrollPosition(){
if( column != null && ! column.is_dispose.get() ){
if( listView.getVisibility() == View.VISIBLE ){ if( listView.getVisibility() == View.VISIBLE ){
column.scroll_save = new ScrollPosition( listView ); column.scroll_save = new ScrollPosition( listView );
}else{ }else{
column.scroll_save = new ScrollPosition( 0, 0 ); column.scroll_save = new ScrollPosition( 0, 0 );
} }
} }
}
@NonNull ScrollPosition getScrollPosition(){ @NonNull ScrollPosition getScrollPosition(){
return new ScrollPosition( listView ); return new ScrollPosition( listView );
} }
void setScrollPosition( @NonNull ScrollPosition sp, final float delta ){ void setScrollPosition( @NonNull ScrollPosition sp, final float delta ){
final ListAdapter last_adapter = listView.getAdapter();
if( column == null || last_adapter == null ) return;
sp.restore( listView ); sp.restore( listView );
listView.postDelayed( new Runnable() { listView.postDelayed( new Runnable() {
@Override public void run(){ @Override public void run(){
if( isPageDestroyed() ) return; if( column == null || listView.getAdapter() != last_adapter ) return;
listView.scrollListBy( (int) ( delta * activity.density ) ); listView.scrollListBy( (int) ( delta * activity.density ) );
} }
}, 20L ); }, 20L );
} }
} }

View File

@ -66,6 +66,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
View btnText = viewRoot.findViewById( R.id.btnText ); View btnText = viewRoot.findViewById( R.id.btnText );
View btnFavouriteAnotherAccount = viewRoot.findViewById( R.id.btnFavouriteAnotherAccount ); View btnFavouriteAnotherAccount = viewRoot.findViewById( R.id.btnFavouriteAnotherAccount );
View btnBoostAnotherAccount = viewRoot.findViewById( R.id.btnBoostAnotherAccount ); View btnBoostAnotherAccount = viewRoot.findViewById( R.id.btnBoostAnotherAccount );
View btnReplyAnotherAccount = viewRoot.findViewById( R.id.btnReplyAnotherAccount );
View btnDelete = viewRoot.findViewById( R.id.btnDelete ); View btnDelete = viewRoot.findViewById( R.id.btnDelete );
View btnReport = viewRoot.findViewById( R.id.btnReport ); View btnReport = viewRoot.findViewById( R.id.btnReport );
Button btnMuteApp = (Button) viewRoot.findViewById( R.id.btnMuteApp ); Button btnMuteApp = (Button) viewRoot.findViewById( R.id.btnMuteApp );
@ -105,9 +106,11 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
if( account_list_non_pseudo_same_instance.isEmpty() ){ if( account_list_non_pseudo_same_instance.isEmpty() ){
btnFavouriteAnotherAccount.setVisibility( View.GONE ); btnFavouriteAnotherAccount.setVisibility( View.GONE );
btnBoostAnotherAccount.setVisibility( View.GONE ); btnBoostAnotherAccount.setVisibility( View.GONE );
btnReplyAnotherAccount.setVisibility( View.GONE );
}else{ }else{
btnFavouriteAnotherAccount.setOnClickListener( this ); btnFavouriteAnotherAccount.setOnClickListener( this );
btnBoostAnotherAccount.setOnClickListener( this ); btnBoostAnotherAccount.setOnClickListener( this );
btnReplyAnotherAccount.setOnClickListener( this );
} }
if( access_info.isPseudo() ){ if( access_info.isPseudo() ){
btnDelete.setVisibility( View.GONE ); btnDelete.setVisibility( View.GONE );
@ -254,44 +257,15 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
break; break;
case R.id.btnFavouriteAnotherAccount: case R.id.btnFavouriteAnotherAccount:
if( status != null ){ activity.openFavouriteFromAnotherAccount( access_info,status );
AccountPicker.pick( activity, false, false
, activity.getString( R.string.account_picker_favourite )
// , account_list_non_pseudo_same_instance
, account_list_non_pseudo
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
activity.performFavourite(
ai
, ! ai.host.equalsIgnoreCase( access_info.host )
, true
, status
, activity.favourite_complete_callback
);
}
} );
}
break; break;
case R.id.btnBoostAnotherAccount: case R.id.btnBoostAnotherAccount:
if( status != null ){ activity.openBoostFromAnotherAccount( access_info,status );
AccountPicker.pick( activity, false, false break;
, activity.getString( R.string.account_picker_boost )
// , account_list_non_pseudo_same_instance case R.id.btnReplyAnotherAccount:
, account_list_non_pseudo activity.openReplyFromAnotherAccount( access_info,status );
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
activity.performBoost(
ai
, ! ai.host.equalsIgnoreCase( access_info.host )
, true
, status
, false
, activity.boost_complete_callback
);
}
} );
}
break; break;
case R.id.btnDelete: case R.id.btnDelete:

View File

@ -78,10 +78,12 @@ class ItemListAdapter extends BaseAdapter implements AdapterView.OnItemClickList
@Override @Override
public void onItemClick( AdapterView< ? > parent, View view, int position, long id ){ public void onItemClick( AdapterView< ? > parent, View view, int position, long id ){
if( column.bSimpleList ){
Object tag = view.getTag(); Object tag = view.getTag();
if( tag instanceof ItemViewHolder ){ if( tag instanceof ItemViewHolder ){
( (ItemViewHolder) tag ).onItemClick( (MyListView) parent, view ); ( (ItemViewHolder) tag ).onItemClick( (MyListView) parent, view );
} }
} }
} }
}

View File

@ -57,6 +57,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
// //
v = viewRoot.findViewById( R.id.btnReply ); v = viewRoot.findViewById( R.id.btnReply );
v.setOnClickListener( this ); v.setOnClickListener( this );
v.setOnLongClickListener( this );
} }
@ -117,9 +118,9 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
break; break;
case R.id.btnReply: case R.id.btnReply:
if( access_info.isPseudo() ){ if( access_info.isPseudo() ){
Utils.showToast( activity, false, R.string.not_available_for_pseudo_account ); activity.openReplyFromAnotherAccount( access_info, status );
}else{ }else{
activity.performReply( access_info, status ); activity.performReply( access_info, status ,false);
} }
break; break;
case R.id.btnBoost: case R.id.btnBoost:
@ -184,6 +185,10 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
activity.openFollowFromAnotherAccount( access_info, status ); activity.openFollowFromAnotherAccount( access_info, status );
break; break;
case R.id.btnReply:
activity.openReplyFromAnotherAccount( access_info, status );
break;
} }
return true; return true;
} }

View File

@ -34,7 +34,7 @@ class TabletColumnPagerAdapter extends RecyclerView.Adapter<TabletColumnViewHold
@Override public TabletColumnViewHolder onCreateViewHolder( ViewGroup parent, int viewType ){ @Override public TabletColumnViewHolder onCreateViewHolder( ViewGroup parent, int viewType ){
View v = mLayoutInflater.inflate( R.layout.page_column, parent, false ); View v = mLayoutInflater.inflate( R.layout.page_column, parent, false );
return new TabletColumnViewHolder( v ); return new TabletColumnViewHolder( activity,v );
} }
@Override public void onBindViewHolder( TabletColumnViewHolder holder, int position ){ @Override public void onBindViewHolder( TabletColumnViewHolder holder, int position ){
@ -45,7 +45,7 @@ class TabletColumnPagerAdapter extends RecyclerView.Adapter<TabletColumnViewHold
holder.itemView.setLayoutParams( lp ); holder.itemView.setLayoutParams( lp );
} }
holder.bind( activity, column_list.get(position), position , column_list.size() ); holder.bind( column_list.get(position), position , column_list.size() );
} }

View File

@ -9,26 +9,22 @@ class TabletColumnViewHolder extends RecyclerView.ViewHolder{
static final LogCategory log = new LogCategory( "TabletColumnViewHolder" ); static final LogCategory log = new LogCategory( "TabletColumnViewHolder" );
ColumnViewHolder vh; final ColumnViewHolder vh;
private int old_position;
TabletColumnViewHolder( View v ){ private int old_position = -1;
TabletColumnViewHolder( ActMain activity, View v ){
super( v ); super( v );
vh =new ColumnViewHolder( activity ,v);
v.findViewById( R.id.vTabletDivider ).setVisibility( View.VISIBLE ); v.findViewById( R.id.vTabletDivider ).setVisibility( View.VISIBLE );
} }
void bind( ActMain activity, Column column,int position,int column_count ){ void bind(Column column,int position,int column_count ){
if( vh != null ){ log.d("bind. %d => %d ",old_position,position);
log.d("destroy #%s",old_position);
vh.onPageDestroy( itemView );
vh = null;
}
old_position = position; vh.onPageDestroy();
log.d("create #%s",position);
vh =new ColumnViewHolder( activity, column); vh.onPageCreate( column, position,column_count );
vh.onPageCreate( itemView,position,column_count );
if( ! column.bFirstInitialized ){ if( ! column.bFirstInitialized ){
column.startLoading(); column.startLoading();

View File

@ -67,7 +67,7 @@ public class TootApiResult {
while( m.find()){ while( m.find()){
String url = m.group(1); String url = m.group(1);
String rel = m.group(2); String rel = m.group(2);
log.d("Link %s,%s",rel,url); // log.d("Link %s,%s",rel,url);
if( "next".equals( rel )) link_older = url; if( "next".equals( rel )) link_older = url;
if( "prev".equals( rel )) link_newer = url; if( "prev".equals( rel )) link_newer = url;
} }

View File

@ -10,7 +10,9 @@ import org.json.JSONObject;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
@ -172,8 +174,9 @@ public class TootStatus extends TootId {
return result; return result;
} }
private static final Pattern reTime = Pattern.compile("\\A(\\d+\\D+\\d+\\D+\\d+\\D+\\d+\\D+\\d+\\D+\\d+\\D+\\d+)"); private static final Pattern reTime = Pattern.compile( "\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)" );
private static final SimpleDateFormat date_format_utc = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault() );
private static final TimeZone tz_utc = TimeZone.getTimeZone( "UTC" );
static long parseTime( LogCategory log, String strTime ){ static long parseTime( LogCategory log, String strTime ){
if( ! TextUtils.isEmpty( strTime ) ){ if( ! TextUtils.isEmpty( strTime ) ){
@ -182,8 +185,17 @@ public class TootStatus extends TootId {
if( ! m.find() ){ if( ! m.find() ){
log.d( "!!invalid time format: %s", strTime ); log.d( "!!invalid time format: %s", strTime );
}else{ }else{
date_format_utc.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); GregorianCalendar g = new GregorianCalendar( tz_utc );
return date_format_utc.parse( m.group( 1 ) ).getTime(); g.set(
Utils.parse_int( m.group( 1 ), 1 ),
Utils.parse_int( m.group( 2 ), 1 ) - 1,
Utils.parse_int( m.group( 3 ), 1 ),
Utils.parse_int( m.group( 4 ), 0 ),
Utils.parse_int( m.group( 5 ), 0 ),
Utils.parse_int( m.group( 6 ), 0 )
);
g.set( Calendar.MILLISECOND, Utils.parse_int( m.group( 7 ), 0 ) );
return g.getTimeInMillis();
} }
}catch( Throwable ex ){// ParseException, ArrayIndexOutOfBoundsException }catch( Throwable ex ){// ParseException, ArrayIndexOutOfBoundsException
ex.printStackTrace(); ex.printStackTrace();
@ -230,7 +242,6 @@ public class TootStatus extends TootId {
} }
} }
public boolean checkMuted( HashSet< String > muted_app, HashSet< String > muted_word ){ public boolean checkMuted( HashSet< String > muted_app, HashSet< String > muted_word ){
// app mute // app mute
@ -263,5 +274,4 @@ public class TootStatus extends TootId {
} }
} }

View File

@ -886,6 +886,10 @@ public class Utils {
return resources.getString( string_id, args ) + String.format( " :%s %s", ex.getClass().getSimpleName(), ex.getMessage() ); return resources.getString( string_id, args ) + String.format( " :%s %s", ex.getClass().getSimpleName(), ex.getMessage() );
} }
public static boolean isMainThread( ){
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
public static void runOnMainThread( @NonNull Runnable proc ){ public static void runOnMainThread( @NonNull Runnable proc ){
if( Looper.getMainLooper().getThread() == Thread.currentThread() ){ if( Looper.getMainLooper().getThread() == Thread.currentThread() ){
proc.run(); proc.run();

View File

@ -133,7 +133,20 @@
android:text="@string/boost_from_another_account" android:text="@string/boost_from_another_account"
android:textAllCaps="false" android:textAllCaps="false"
/> />
<Button
android:id="@+id/btnReplyAnotherAccount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/btn_bg_transparent"
android:gravity="start|center_vertical"
android:minHeight="32dp"
android:paddingBottom="4dp"
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:text="@string/reply_from_another_account"
android:textAllCaps="false"
/>
<Button <Button
android:id="@+id/btnDelete" android:id="@+id/btnDelete"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation" tools:ignore="MissingTranslation"
> >
<string name="abc_action_bar_home_description">Revenir à l\'accueil</string> <string name="abc_action_bar_home_description">Revenir à l\'accueil</string>
<string name="abc_action_bar_home_description_format">%1$s, %2$s</string> <string name="abc_action_bar_home_description_format">%1$s, %2$s</string>
<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string> <string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>
@ -332,4 +333,5 @@
<string name="with_attachment">Avec pièce(s) jointe(s)</string> <string name="with_attachment">Avec pièce(s) jointe(s)</string>
<string name="word_was_muted">Ce mot a été interdit.</string> <string name="word_was_muted">Ce mot a été interdit.</string>
<string name="your_statuses">Vos statuts</string> <string name="your_statuses">Vos statuts</string>
</resources> </resources>

View File

@ -476,11 +476,11 @@
<!----> <!---->
<string name="confirm_follow_request_who_from">%2$s から鍵付きユーザ %1$s にフォローリクエストを送りますか?</string> <string name="confirm_follow_request_who_from">%2$s から鍵付きユーザ %1$s にフォローリクエストを送りますか?</string>
<!----> <!---->
<string name="confirm_follow_who_from">%2$s から %1$s をフォローしますか?</string> <string name="confirm_follow_who_from">%2$s %1$s をフォローしますか?</string>
<!----> <!---->
<string name="confirm_post_from">%1$s から投稿します。よろしいですか?</string> <string name="confirm_post_from">%1$s から投稿します。よろしいですか?</string>
<!----> <!---->
<string name="confirm_unfollow_who_from">%2$s から %1$s をフォロー解除しますか?</string> <string name="confirm_unfollow_who_from">%2$s %1$s をフォロー解除しますか?</string>
<!----> <!---->
<string name="dont_confirm_again">次回から確認しない</string> <string name="dont_confirm_again">次回から確認しない</string>
<!----> <!---->
@ -599,4 +599,6 @@
<string name="disable_tablet_mode">タブレットモードを使わない(アプリ再起動が必要)</string> <string name="disable_tablet_mode">タブレットモードを使わない(アプリ再起動が必要)</string>
<string name="media_thumbnail_height">添付メディアのサムネイルの高さ(デフォルト=64(dp)、アプリ再起動が必要)</string> <string name="media_thumbnail_height">添付メディアのサムネイルの高さ(デフォルト=64(dp)、アプリ再起動が必要)</string>
<string name="minimum_column_width">カラム最小幅(デフォルト=300(dp)、アプリ再起動が必要)</string> <string name="minimum_column_width">カラム最小幅(デフォルト=300(dp)、アプリ再起動が必要)</string>
<string name="account_picker_reply">どのアカウントで返信しますか?</string>
<string name="reply_from_another_account">別アカウントで返信</string>
</resources> </resources>

View File

@ -309,4 +309,6 @@
<string name="disable_tablet_mode">Disable tablet mode (app restart required)</string> <string name="disable_tablet_mode">Disable tablet mode (app restart required)</string>
<string name="minimum_column_width">Minimum column width (default=300(dp),app restart required)</string> <string name="minimum_column_width">Minimum column width (default=300(dp),app restart required)</string>
<string name="media_thumbnail_height">Media Thumbnail height (default=64(dp), app restart required)</string> <string name="media_thumbnail_height">Media Thumbnail height (default=64(dp), app restart required)</string>
<string name="account_picker_reply">Which account do you reply from?</string>
<string name="reply_from_another_account">Reply from another account</string>
</resources> </resources>