カラム設定にブースト非表示、返信非表示、正規表現フィルタ、通知の削除を追加。発言の公開範囲が非公開以下だった場合にブーストボタンの表示を変更

This commit is contained in:
tateisu 2017-04-28 14:19:49 +09:00
parent 6689da0bb3
commit 943498928b
14 changed files with 584 additions and 211 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter" applicationId "jp.juggler.subwaytooter"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 25 targetSdkVersion 25
versionCode 16 versionCode 17
versionName "0.1.6" versionName "0.1.7"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }

View File

@ -28,7 +28,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan|stateAlwaysHidden" android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@ -8,10 +8,10 @@ import android.content.SharedPreferences;
import android.net.Uri; 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.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v4.os.AsyncTaskCompat; import android.support.v4.os.AsyncTaskCompat;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
@ -72,6 +72,7 @@ public class ActMain extends AppCompatActivity
float density; float density;
SharedPreferences pref; SharedPreferences pref;
Handler handler;
@Override @Override
protected void onCreate( Bundle savedInstanceState ){ protected void onCreate( Bundle savedInstanceState ){
@ -82,6 +83,7 @@ public class ActMain extends AppCompatActivity
requestWindowFeature( Window.FEATURE_NO_TITLE ); requestWindowFeature( Window.FEATURE_NO_TITLE );
pref = Pref.pref( this ); pref = Pref.pref( this );
handler = new Handler( );
initUI(); initUI();
@ -470,7 +472,7 @@ public class ActMain extends AppCompatActivity
if( account != null ){ if( account != null ){
Column column = addColumn( account, Column.TYPE_NOTIFICATIONS ); Column column = addColumn( account, Column.TYPE_NOTIFICATIONS );
if( ! column.bInitialLoading ){ if( ! column.bInitialLoading ){
column.reload(); column.startLoading();
} }
} }
}catch( Throwable ex ){ }catch( Throwable ex ){
@ -552,7 +554,7 @@ public class ActMain extends AppCompatActivity
this.host = client.instance; this.host = client.instance;
TootApiResult result = client.authorize2( uri.toString(), code ); TootApiResult result = client.authorize2( code );
if( result != null && result.object != null ){ if( result != null && result.object != null ){
// taは使い捨てなので生成に使うLinkClickContextはダミーで問題ない // taは使い捨てなので生成に使うLinkClickContextはダミーで問題ない
LinkClickContext lcc = new LinkClickContext() { LinkClickContext lcc = new LinkClickContext() {
@ -592,7 +594,7 @@ public class ActMain extends AppCompatActivity
// 自動でリロードする // 自動でリロードする
for( Column c : pager_adapter.column_list ){ for( Column c : pager_adapter.column_list ){
if( c.access_info.acct.equals( sa.acct ) ){ if( c.access_info.acct.equals( sa.acct ) ){
c.reload(); c.startLoading();
} }
} }
} }
@ -714,6 +716,7 @@ public class ActMain extends AppCompatActivity
addColumn( access_info, Column.TYPE_HASHTAG, tag ); addColumn( access_info, Column.TYPE_HASHTAG, tag );
} }
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
interface GetAccountCallback { interface GetAccountCallback {
@ -1944,4 +1947,73 @@ public class ActMain extends AppCompatActivity
startActivityForResult( new Intent( this, ActAbout.class ), REQUEST_APP_ABOUT ); startActivityForResult( new Intent( this, ActAbout.class ), REQUEST_APP_ABOUT );
} }
public void deleteNotification( boolean bConfirmed, final SavedAccount target_account ){
if( ! bConfirmed ){
new AlertDialog.Builder( this )
.setMessage( R.string.confirm_delete_notification )
.setNegativeButton( R.string.cancel, null )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
deleteNotification( true, target_account );
}
} )
.show();
return;
}
new AsyncTask< Void, Void, TootApiResult >() {
@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( String s ){
}
} );
client.setAccount( target_account );
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "" // 空データ
) );
TootApiResult result = client.request( "/api/v1/notifications/clear" , request_builder );
return result;
}
@Override
protected void onCancelled( TootApiResult result ){
onPostExecute( null );
}
@Override
protected void onPostExecute( TootApiResult result ){
if( result == null ){
//cancelled.
}else if( result.object != null ){
// ok. empty object will be returned.
for( Column column : pager_adapter.column_list ){
if( column.type == Column.TYPE_NOTIFICATIONS
&& column.access_info.acct.equals( target_account.acct )
){
column.removeNotifications();
}
}
Utils.showToast( ActMain.this, false, R.string.delete_succeeded );
}else{
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}
} }

View File

@ -47,6 +47,7 @@ public class AlarmService extends IntentService {
static final String KEY_TIME = "<>time"; static final String KEY_TIME = "<>time";
private static final String ACTION_DATA_INJECTED = "data_injected"; private static final String ACTION_DATA_INJECTED = "data_injected";
private static final String EXTRA_DB_ID = "db_id"; private static final String EXTRA_DB_ID = "db_id";
private static final String ACTION_DATA_DELETED = "data_deleted";
public AlarmService(){ public AlarmService(){
// name: Used to name the worker thread, important only for debugging. // name: Used to name the worker thread, important only for debugging.
@ -94,7 +95,9 @@ public class AlarmService extends IntentService {
String action = intent.getAction(); String action = intent.getAction();
log.d("onHandleIntent action=%s",action); log.d("onHandleIntent action=%s",action);
if( ACTION_DATA_INJECTED.equals( action ) ){ if( ACTION_DATA_DELETED.equals( action ) ){
deleteCacheData(intent.getLongExtra( EXTRA_DB_ID ,-1L));
}else if( ACTION_DATA_INJECTED.equals( action ) ){
processInjectedData(); processInjectedData();
}else if( AlarmReceiver.ACTION_FROM_RECEIVER.equals( action ) ){ }else if( AlarmReceiver.ACTION_FROM_RECEIVER.equals( action ) ){
WakefulBroadcastReceiver.completeWakefulIntent( intent ); WakefulBroadcastReceiver.completeWakefulIntent( intent );
@ -181,7 +184,6 @@ public class AlarmService extends IntentService {
} }
private static class Data { private static class Data {
SavedAccount access_info; SavedAccount access_info;
TootNotification notification; TootNotification notification;
@ -502,4 +504,23 @@ public class AlarmService extends IntentService {
nr.save(); nr.save();
} }
} }
public static void dataRemoved( Context context, long db_id ){
Intent intent = new Intent( context, AlarmService.class );
intent.putExtra( EXTRA_DB_ID,db_id );
intent.setAction( ACTION_DATA_DELETED );
context.startService( intent );
}
private void deleteCacheData( long db_id ){
SavedAccount account = SavedAccount.loadAccount( log,db_id );
if( account == null ) return;
NotificationTracking nr = NotificationTracking.load( db_id );
nr.last_data = new JSONArray().toString();
nr.save();
}
} }

View File

@ -35,7 +35,7 @@ import jp.juggler.subwaytooter.util.Utils;
class Column { class Column {
private static final LogCategory log = new LogCategory( "Column" ); private static final LogCategory log = new LogCategory( "Column" );
private static Object getParamAt( Object[] params, int idx ){ private static Object getParamAt( Object[] params, int idx ){
if( params == null || idx >= params.length ){ if( params == null || idx >= params.length ){
throw new IndexOutOfBoundsException( "getParamAt idx=" + idx ); throw new IndexOutOfBoundsException( "getParamAt idx=" + idx );
@ -72,8 +72,12 @@ class Column {
private static final String KEY_ACCOUNT_ROW_ID = "account_id"; private static final String KEY_ACCOUNT_ROW_ID = "account_id";
private static final String KEY_TYPE = "type"; private static final String KEY_TYPE = "type";
private static final String KEY_WITH_ATTACHMENT = "with_attachment";
static final String KEY_DONT_CLOSE = "dont_close"; static final String KEY_DONT_CLOSE = "dont_close";
private static final String KEY_WITH_ATTACHMENT = "with_attachment";
private static final String KEY_DONT_SHOW_BOOST = "dont_show_boost";
private static final String KEY_DONT_SHOW_REPLY = "dont_show_reply";
private static final String KEY_REGEX_TEXT = "regex_text";
private static final String KEY_PROFILE_ID = "profile_id"; private static final String KEY_PROFILE_ID = "profile_id";
@ -105,8 +109,13 @@ class Column {
final int type; final int type;
boolean with_attachment;
boolean dont_close; boolean dont_close;
boolean with_attachment;
boolean dont_show_boost;
boolean dont_show_reply;
String regex_text;
private long profile_id; private long profile_id;
volatile TootAccount who_account; volatile TootAccount who_account;
@ -156,8 +165,11 @@ class Column {
void encodeJSON( JSONObject item, int old_index ) throws JSONException{ void encodeJSON( JSONObject item, int old_index ) throws JSONException{
item.put( KEY_ACCOUNT_ROW_ID, access_info.db_id ); item.put( KEY_ACCOUNT_ROW_ID, access_info.db_id );
item.put( KEY_TYPE, type ); item.put( KEY_TYPE, type );
item.put( KEY_WITH_ATTACHMENT, with_attachment );
item.put( KEY_DONT_CLOSE, dont_close ); item.put( KEY_DONT_CLOSE, dont_close );
item.put( KEY_WITH_ATTACHMENT, with_attachment );
item.put( KEY_DONT_SHOW_BOOST, dont_show_boost );
item.put( KEY_DONT_SHOW_REPLY, dont_show_reply );
item.put( KEY_REGEX_TEXT, regex_text );
switch( type ){ switch( type ){
case TYPE_CONVERSATION: case TYPE_CONVERSATION:
@ -189,8 +201,11 @@ class Column {
if( ac == null ) throw new RuntimeException( "missing account" ); if( ac == null ) throw new RuntimeException( "missing account" );
this.access_info = ac; this.access_info = ac;
this.type = src.optInt( KEY_TYPE ); this.type = src.optInt( KEY_TYPE );
this.with_attachment = src.optBoolean( KEY_WITH_ATTACHMENT );
this.dont_close = src.optBoolean( KEY_DONT_CLOSE ); this.dont_close = src.optBoolean( KEY_DONT_CLOSE );
this.with_attachment = src.optBoolean( KEY_WITH_ATTACHMENT );
this.dont_show_boost = src.optBoolean( KEY_DONT_SHOW_BOOST );
this.dont_show_reply = src.optBoolean( KEY_DONT_SHOW_REPLY );
this.regex_text = Utils.optStringX(src, KEY_REGEX_TEXT);
switch( type ){ switch( type ){
@ -315,6 +330,7 @@ class Column {
} }
} }
interface StatusEntryCallback { interface StatusEntryCallback {
void onIterate( TootStatus status ); void onIterate( TootStatus status );
} }
@ -502,20 +518,62 @@ class Column {
final ArrayList< Object > list_data = new ArrayList<>(); final ArrayList< Object > list_data = new ArrayList<>();
void reload(){
list_data.clear();
startLoading();
}
private static boolean hasMedia( TootStatus status ){ private static boolean hasMedia( TootStatus status ){
if( status == null ) return false; if( status == null ) return false;
TootAttachment.List list = status.media_attachments; TootAttachment.List list = status.media_attachments;
return ! ( list == null || list.isEmpty() ); return ! ( list == null || list.isEmpty() );
} }
private void startLoading(){ private boolean isFiltered(){
return ( with_attachment
|| dont_show_boost
|| dont_show_reply
|| !TextUtils.isEmpty( regex_text )
);
}
private void addWithFilter( ArrayList< Object > dst, TootStatus.List src ){
if( ! isFiltered() ){
dst.addAll( src );
return;
}
Pattern pattern = null;
if( ! TextUtils.isEmpty( regex_text ) ){
try{
pattern = Pattern.compile( regex_text );
}catch( Throwable ex ){
ex.printStackTrace();
}
}
for( TootStatus status : src ){
if( with_attachment ){
if( ! hasMedia( status ) && ! hasMedia( status.reblog ) ) continue;
}
if( dont_show_boost ){
if( status.reblog != null ) continue;
}
if( dont_show_reply ){
if( status.in_reply_to_id != null
|| ( status.reblog != null && status.reblog.in_reply_to_id != null )
) continue;
}
if( pattern != null ){
if( status.reblog != null ){
if( pattern.matcher( status.reblog.decoded_content.toString() ).find() )
continue;
}else{
if( pattern.matcher( status.decoded_content.toString() ).find() ) continue;
}
}
dst.add( status );
}
}
void startLoading(){
cancelLastTask(); cancelLastTask();
list_data.clear();
mRefreshLoadingError = null; mRefreshLoadingError = null;
bRefreshLoading = false; bRefreshLoading = false;
mInitialLoadingError = null; mInitialLoadingError = null;
@ -543,42 +601,27 @@ class Column {
// //
TootStatus.List src = TootStatus.parseList( log, access_info, result.array ); TootStatus.List src = TootStatus.parseList( log, access_info, result.array );
list_tmp = new ArrayList<>( src.size() ); list_tmp = new ArrayList<>( src.size() );
if( ! with_attachment ){ addWithFilter(list_tmp,src);
list_tmp.addAll( src );
}else{
for( TootStatus status : src ){
if( hasMedia( status ) || hasMedia( status.reblog ) )
list_tmp.add( status );
}
}
// //
if( max_id != null && with_attachment ){ char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' ); while( isFiltered() && max_id != null && list_tmp.size() < 50 ){
for( ; ; ){ if( client.isCancelled() ) break;
if( src.isEmpty() ){ if( src.isEmpty() ){
// 直前のリクエストが空のリストを返したら諦める // 直前のリクエストが空のリストを返したら諦める
break; break;
}
if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// タイムアウト
break;
}
String path = path_base + delimiter + "max_id=" + max_id;
TootApiResult result2 = client.request( path );
if( result2 == null || result2.array == null ) break;
if( ! saveRangeEnd( result2 ) ) break;
src = TootStatus.parseList( log, access_info, result2.array );
if( ! with_attachment ){
list_tmp.addAll( src );
}else{
for( TootStatus status : src ){
if( hasMedia( status ) || hasMedia( status.reblog ) )
list_tmp.add( status );
}
}
} }
if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// タイムアウト
break;
}
String path = path_base + delimiter + "max_id=" + max_id;
TootApiResult result2 = client.request( path );
if( result2 == null || result2.array == null ) break;
if( ! saveRangeEnd( result2 ) ) break;
src = TootStatus.parseList( log, access_info, result2.array );
addWithFilter(list_tmp,src);
} }
} }
return result; return result;
@ -1022,57 +1065,46 @@ class Column {
TootStatus.List src = TootStatus.parseList( log, access_info, result.array ); TootStatus.List src = TootStatus.parseList( log, access_info, result.array );
list_tmp = new ArrayList<>(); list_tmp = new ArrayList<>();
if( ! with_attachment ){ addWithFilter(list_tmp,src);
list_tmp.addAll( src );
}else{
for( TootStatus status : src ){
if( hasMedia( status ) || hasMedia( status.reblog ) )
list_tmp.add( status );
}
}
if( bBottom ){ if( bBottom ){
if( with_attachment ){ while( list_tmp.size() < 50 ){
for( ; ; ){ if( client.isCancelled() ) break;
// max_id だけを指定した場合必ずlimit個のデータが帰ってくるとは限らない
// 直前のデータが0個なら終了とみなすしかなさそう // max_id だけを指定した場合必ずlimit個のデータが帰ってくるとは限らない
if( src.isEmpty() ){ // 直前のデータが0個なら終了とみなすしかなさそう
log.d( "refresh-status-bottom: previous size == 0." ); if( src.isEmpty() ){
break; log.d( "refresh-status-bottom: previous size == 0." );
} break;
}
if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// タイムアウト if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
log.d( "refresh-status-bottom: loop timeout." ); // タイムアウト
break; log.d( "refresh-status-bottom: loop timeout." );
} break;
}
String path = path_base + delimiter + "max_id=" + max_id;
TootApiResult result2 = client.request( path ); String path = path_base + delimiter + "max_id=" + max_id;
if( result2 == null || result2.array == null ){ TootApiResult result2 = client.request( path );
log.d( "refresh-status-bottom: error or cancelled." ); if( result2 == null || result2.array == null ){
break; log.d( "refresh-status-bottom: error or cancelled." );
} break;
}
src = TootStatus.parseList( log, access_info, result2.array );
src = TootStatus.parseList( log, access_info, result2.array );
if( ! with_attachment ){
list_tmp.addAll( src ); addWithFilter(list_tmp,src);
}else{
for( TootStatus status : src ){ if( ! saveRangeEnd( result2 ) ){
if( hasMedia( status ) || hasMedia( status.reblog ) ) log.d( "refresh-status-bottom: saveRangeEnd failed." );
list_tmp.add( status ); break;
}
}
if( ! saveRangeEnd( result2 ) ){
log.d( "refresh-status-bottom: saveRangeEnd failed." );
break;
}
} }
} }
}else{ }else{
for( ; ; ){ // 頭の方を読む時は隙間を減らすためフィルタの有無に関係なく繰り返しを行う
while( list_tmp.size() < 50 ){
if( client.isCancelled() ) break;
// max_id だけを指定した場合必ずlimit個のデータが帰ってくるとは限らない // max_id だけを指定した場合必ずlimit個のデータが帰ってくるとは限らない
// 直前のデータが0個なら終了とみなすしかなさそう // 直前のデータが0個なら終了とみなすしかなさそう
if( src.isEmpty() ){ if( src.isEmpty() ){
@ -1102,14 +1134,7 @@ class Column {
} }
src = TootStatus.parseList( log, access_info, result2.array ); src = TootStatus.parseList( log, access_info, result2.array );
if( ! with_attachment ){ addWithFilter( list_tmp,src );
list_tmp.addAll( src );
}else{
for( TootStatus status : src ){
if( hasMedia( status ) || hasMedia( status.reblog ) )
list_tmp.add( status );
}
}
} }
} }
} }
@ -1409,6 +1434,8 @@ class Column {
TootApiResult result = null; TootApiResult result = null;
for( ; ; ){ for( ; ; ){
if( client.isCancelled() ) break;
if( result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){ if( result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// タイムアウト // タイムアウト
// 隙間が残る // 隙間が残る
@ -1444,14 +1471,7 @@ class Column {
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである // 隙間の最新のステータスIDは取得データ末尾のステータスIDである
max_id = Long.toString( src.get( src.size() - 1 ).id ); max_id = Long.toString( src.get( src.size() - 1 ).id );
if( ! with_attachment ){ addWithFilter( list_tmp,src );
list_tmp.addAll( src );
}else{
for( TootStatus status : src ){
if( hasMedia( status ) || hasMedia( status.reblog ) )
list_tmp.add( status );
}
}
} }
return result; return result;
} }
@ -1594,4 +1614,21 @@ class Column {
AsyncTaskCompat.executeParallel( task ); AsyncTaskCompat.executeParallel( task );
return null; return null;
} }
void removeNotifications(){
cancelLastTask();
list_data.clear();
mRefreshLoadingError = null;
bRefreshLoading = false;
mInitialLoadingError = null;
bInitialLoading = false;
max_id = null;
since_id = null;
fireVisualCallback();
AlarmService.dataRemoved(activity,access_info.db_id);
}
} }

View File

@ -3,7 +3,9 @@ package jp.juggler.subwaytooter;
import android.content.Context; import android.content.Context;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
@ -27,6 +29,8 @@ import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirec
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jp.juggler.subwaytooter.api.entity.TootAccount; import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootAttachment; import jp.juggler.subwaytooter.api.entity.TootAttachment;
@ -53,7 +57,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
this.column = column; this.column = column;
} }
public boolean isPageDestroyed(){ private boolean isPageDestroyed(){
return is_destroyed.get() || activity.isFinishing(); return is_destroyed.get() || activity.isFinishing();
} }
@ -74,6 +78,8 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
private EditText etSearch; private EditText etSearch;
private CheckBox cbResolve; private CheckBox cbResolve;
private View llColumnSetting; private View llColumnSetting;
private EditText etRegexFilter;
private TextView tvRegexFilterError;
void onPageCreate( View root, int page_idx, int page_count ){ void onPageCreate( View root, int page_idx, int page_count ){
log.d( "onPageCreate:%s", column.getColumnName( true ) ); log.d( "onPageCreate:%s", column.getColumnName( true ) );
@ -104,10 +110,10 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
listView.setFastScrollEnabled( ! Pref.pref( activity ).getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, false ) ); listView.setFastScrollEnabled( ! Pref.pref( activity ).getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, false ) );
boolean bAllowMediaOnly; boolean bAllowFilter;
switch( column.type ){ switch( column.type ){
default: default:
bAllowMediaOnly = true; bAllowFilter = true;
break; break;
case Column.TYPE_SEARCH: case Column.TYPE_SEARCH:
case Column.TYPE_CONVERSATION: case Column.TYPE_CONVERSATION:
@ -115,23 +121,85 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
case Column.TYPE_BLOCKS: case Column.TYPE_BLOCKS:
case Column.TYPE_MUTES: case Column.TYPE_MUTES:
case Column.TYPE_NOTIFICATIONS: case Column.TYPE_NOTIFICATIONS:
bAllowMediaOnly = false; bAllowFilter = false;
break; break;
} }
boolean bAllowFilterBoost;
switch( column.type ){
default:
bAllowFilterBoost = false;
break;
case Column.TYPE_HOME:
case Column.TYPE_PROFILE:
bAllowFilterBoost = true;
break;
}
View btnColumnSetting = root.findViewById( R.id.btnColumnSetting ); View btnColumnSetting = root.findViewById( R.id.btnColumnSetting );
llColumnSetting = root.findViewById( R.id.llColumnSetting ); llColumnSetting = root.findViewById( R.id.llColumnSetting );
btnColumnSetting.setVisibility( View.VISIBLE ); btnColumnSetting.setVisibility( View.VISIBLE );
btnColumnSetting.setOnClickListener( this ); btnColumnSetting.setOnClickListener( this );
llColumnSetting.setVisibility( View.GONE ); llColumnSetting.setVisibility( View.GONE );
CheckBox cbWithAttachment = (CheckBox) root.findViewById( R.id.cbWithAttachment ); CheckBox cb;
cbWithAttachment.setChecked( column.with_attachment ); cb = (CheckBox) root.findViewById( R.id.cbDontCloseColumn );
cbWithAttachment.setOnCheckedChangeListener( this ); cb.setChecked( column.dont_close );
cbWithAttachment.setEnabled( bAllowMediaOnly ); cb.setOnCheckedChangeListener( this );
CheckBox cbDontCloseColumn = (CheckBox) root.findViewById( R.id.cbDontCloseColumn ); cb = (CheckBox) root.findViewById( R.id.cbWithAttachment );
cbDontCloseColumn.setChecked( column.dont_close ); cb.setChecked( column.with_attachment );
cbDontCloseColumn.setOnCheckedChangeListener( this ); 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 );
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 );
// 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.type != Column.TYPE_NOTIFICATIONS ){
button.setVisibility( View.GONE );
}else{
button.setVisibility( View.VISIBLE );
button.setOnClickListener( this );
}
if( column.type != Column.TYPE_SEARCH ){ if( column.type != Column.TYPE_SEARCH ){
llSearch.setVisibility( View.GONE ); llSearch.setVisibility( View.GONE );
@ -168,19 +236,61 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
onVisualColumn(); onVisualColumn();
} }
private final Runnable proc_start_filter = new Runnable() {
@Override public void run(){
if( isPageDestroyed() ) return;
if( isRegexValid() ){
column.regex_text = etRegexFilter.getText().toString();
activity.saveColumnList();
column.startLoading();
}
}
};
private boolean isRegexValid(){
String s = etRegexFilter.getText().toString();
if( s.length() == 0 ){
tvRegexFilterError.setText( "" );
return true;
}
try{
@SuppressWarnings("unused") Matcher m = Pattern.compile( s ).matcher( "" );
tvRegexFilterError.setText( "" );
return true;
}catch( Throwable ex ){
String message = ex.getMessage();
if( TextUtils.isEmpty( message ) )
message = Utils.formatError( ex, activity.getResources(), R.string.regex_error );
tvRegexFilterError.setText( message );
return false;
}
}
@Override public void onCheckedChanged( CompoundButton view, boolean isChecked ){ @Override public void onCheckedChanged( CompoundButton view, boolean isChecked ){
switch( view.getId() ){ switch( view.getId() ){
case R.id.cbWithAttachment:
column.with_attachment = isChecked;
activity.saveColumnList();
column.reload();
break;
case R.id.cbDontCloseColumn: case R.id.cbDontCloseColumn:
column.dont_close = isChecked; column.dont_close = isChecked;
activity.saveColumnList(); activity.saveColumnList();
break; break;
case R.id.cbWithAttachment:
column.with_attachment = isChecked;
activity.saveColumnList();
column.startLoading();
break;
case R.id.cbDontShowBoost:
column.dont_show_boost = isChecked;
activity.saveColumnList();
column.startLoading();
break;
case R.id.cbDontShowReply:
column.dont_show_reply = isChecked;
activity.saveColumnList();
column.startLoading();
break;
} }
} }
@ -198,14 +308,14 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
etSearch.setText( column.search_query ); etSearch.setText( column.search_query );
cbResolve.setChecked( column.search_resolve ); cbResolve.setChecked( column.search_resolve );
} }
column.reload(); column.startLoading();
break; break;
case R.id.btnSearch: case R.id.btnSearch:
hideKeyboard( etSearch ); hideKeyboard( etSearch );
column.search_query = etSearch.getText().toString().trim(); column.search_query = etSearch.getText().toString().trim();
column.search_resolve = cbResolve.isChecked(); column.search_resolve = cbResolve.isChecked();
column.reload(); column.startLoading();
break; break;
case R.id.llColumnHeader: case R.id.llColumnHeader:
@ -215,6 +325,10 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
case R.id.btnColumnSetting: case R.id.btnColumnSetting:
llColumnSetting.setVisibility( llColumnSetting.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE ); llColumnSetting.setVisibility( llColumnSetting.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE );
break; break;
case R.id.btnDeleteNotification:
activity.deleteNotification( false, column.access_info );
break;
} }
} }
@ -350,6 +464,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
final Button btnStatusCount; final Button btnStatusCount;
final View btnMore; final View btnMore;
final TextView tvNote; final TextView tvNote;
TootAccount who; TootAccount who;
SavedAccount access_info; SavedAccount access_info;
@ -420,17 +535,17 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
case R.id.btnFollowing: case R.id.btnFollowing:
column.profile_tab = Column.TAB_FOLLOWING; column.profile_tab = Column.TAB_FOLLOWING;
column.reload(); column.startLoading();
break; break;
case R.id.btnFollowers: case R.id.btnFollowers:
column.profile_tab = Column.TAB_FOLLOWERS; column.profile_tab = Column.TAB_FOLLOWERS;
column.reload(); column.startLoading();
break; break;
case R.id.btnStatusCount: case R.id.btnStatusCount:
column.profile_tab = Column.TAB_STATUS; column.profile_tab = Column.TAB_STATUS;
column.reload(); column.startLoading();
break; break;
case R.id.btnMore: case R.id.btnMore:
@ -696,7 +811,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
void showBoost( TootAccount who, long time, int icon_attr_id, CharSequence text ){ void showBoost( TootAccount who, long time, int icon_attr_id, CharSequence text ){
account_boost = who; account_boost = who;
llBoosted.setVisibility( View.VISIBLE ); llBoosted.setVisibility( View.VISIBLE );
ivBoosted.setImageResource( Styler.getAttributeResourceId(activity,icon_attr_id) ); ivBoosted.setImageResource( Styler.getAttributeResourceId( activity, icon_attr_id ) );
tvBoostedTime.setText( TootStatus.formatTime( time ) ); tvBoostedTime.setText( TootStatus.formatTime( time ) );
tvBoostedAcct.setText( access_info.getFullAcct( who ) ); tvBoostedAcct.setText( access_info.getFullAcct( who ) );
tvBoosted.setText( text ); tvBoosted.setText( text );
@ -709,7 +824,7 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
tvFollowerName.setText( who.display_name ); tvFollowerName.setText( who.display_name );
tvFollowerAcct.setText( access_info.getFullAcct( who ) ); tvFollowerAcct.setText( access_info.getFullAcct( who ) );
btnFollow.setImageResource( Styler.getAttributeResourceId( activity,R.attr.ic_account_add )); btnFollow.setImageResource( Styler.getAttributeResourceId( activity, R.attr.ic_account_add ) );
} }
private void showStatus( ActMain activity, TootStatus status, SavedAccount account ){ private void showStatus( ActMain activity, TootStatus status, SavedAccount account ){
@ -763,44 +878,38 @@ class ColumnViewHolder implements View.OnClickListener, Column.VisualCallback, S
btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE ); btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE );
} }
Drawable d; int color_normal = Styler.getAttributeColor( activity, R.attr.colorImageButton );
int color_normal = Styler.getAttributeColor( activity,R.attr.colorImageButton ); int color_accent = Styler.getAttributeColor( activity, R.attr.colorImageButtonAccent );
int color_accent = Styler.getAttributeColor( activity,R.attr.colorImageButtonAccent );
if( activity.isBusyBoost( account, status ) ){ if( TootStatus.VISIBILITY_DIRECT.equals( status.visibility ) ){
d = Styler.getAttributeDrawable( activity,R.attr.btn_refresh ).mutate(); setButton( btnBoost, false, color_accent, R.attr.ic_mail, "" );
d.setColorFilter( color_normal, PorterDuff.Mode.SRC_ATOP ); }else if( TootStatus.VISIBILITY_PRIVATE.equals( status.visibility ) ){
btnBoost.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null ); setButton( btnBoost, false, color_accent, R.attr.ic_lock, "" );
btnBoost.setText( "?" ); }else if( activity.isBusyBoost( account, status ) ){
btnBoost.setTextColor( color_normal ); setButton( btnBoost, false, color_normal, R.attr.btn_refresh, "?" );
}else{ }else{
int color = ( status.reblogged ? color_accent : color_normal ); int color = ( status.reblogged ? color_accent : color_normal );
d = Styler.getAttributeDrawable( activity,R.attr.btn_boost ).mutate(); setButton( btnBoost, true, color, R.attr.btn_boost, Long.toString( status.reblogs_count ) );
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
btnBoost.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null );
btnBoost.setText( Long.toString( status.reblogs_count ) );
btnBoost.setTextColor( color );
} }
if( activity.isBusyFav( account, status ) ){ if( activity.isBusyFav( account, status ) ){
d = Styler.getAttributeDrawable( activity,R.attr.btn_refresh ).mutate(); setButton( btnFavourite, false, color_normal, R.attr.btn_refresh, "?" );
d.setColorFilter( color_normal, PorterDuff.Mode.SRC_ATOP );
btnFavourite.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null );
btnFavourite.setText( "?" );
btnFavourite.setTextColor( color_normal );
}else{ }else{
int color = ( status.favourited ? color_accent : color_normal ); int color = ( status.favourited ? color_accent : color_normal );
d = Styler.getAttributeDrawable( activity,R.attr.btn_favourite ).mutate(); setButton( btnFavourite, true, color, R.attr.btn_favourite, Long.toString( status.favourites_count ) );
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
btnFavourite.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null );
btnFavourite.setText( Long.toString( status.favourites_count ) );
btnFavourite.setTextColor( color );
} }
} }
private void setButton( Button b, boolean enabled, int color, int icon_attr, String text ){
Drawable d = Styler.getAttributeDrawable( activity, icon_attr ).mutate();
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
b.setCompoundDrawablesRelativeWithIntrinsicBounds( d, null, null, null );
b.setText( text );
b.setTextColor( color );
b.setEnabled( enabled );
}
private void showContent( boolean shown ){ private void showContent( boolean shown ){
btnContentWarning.setText( shown ? R.string.hide : R.string.show ); btnContentWarning.setText( shown ? R.string.hide : R.string.show );
llContents.setVisibility( shown ? View.VISIBLE : View.GONE ); llContents.setVisibility( shown ? View.VISIBLE : View.GONE );

View File

@ -1,7 +1,6 @@
package jp.juggler.subwaytooter.api; package jp.juggler.subwaytooter.api;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
@ -25,7 +24,7 @@ import okhttp3.Response;
public class TootApiClient { public class TootApiClient {
private static final LogCategory log = new LogCategory( "TootApiClient" ); private static final LogCategory log = new LogCategory( "TootApiClient" );
static final OkHttpClient ok_http_client = App1.ok_http_client; private static final OkHttpClient ok_http_client = App1.ok_http_client;
public interface Callback { public interface Callback {
boolean isApiCancelled(); boolean isApiCancelled();
@ -57,6 +56,10 @@ public class TootApiClient {
this.account = account; this.account = account;
} }
public boolean isCancelled(){
return callback.isApiCancelled();
}
public static final MediaType MEDIA_TYPE_FORM_URL_ENCODED = MediaType.parse( "application/x-www-form-urlencoded" ); public static final MediaType MEDIA_TYPE_FORM_URL_ENCODED = MediaType.parse( "application/x-www-form-urlencoded" );
public TootApiResult request( String path ){ public TootApiResult request( String path ){
@ -66,17 +69,17 @@ public class TootApiClient {
public TootApiResult request( String path, Request.Builder request_builder ){ public TootApiResult request( String path, Request.Builder request_builder ){
log.d( "request: %s", path ); log.d( "request: %s", path );
TootApiResult result = request_sub( path, request_builder ); TootApiResult result = request_sub( path, request_builder );
if( result.error != null ){ if( result != null && result.error != null ){
log.d( "error: %s", result.error ); log.d( "error: %s", result.error );
} }
return result; return result;
} }
static final String KEY_AUTH_VERSION = "SubwayTooterAuthVersion"; private static final String KEY_AUTH_VERSION = "SubwayTooterAuthVersion";
static final int AUTH_VERSION = 1; private static final int AUTH_VERSION = 1;
static final String REDIRECT_URL = "subwaytooter://oauth"; private static final String REDIRECT_URL = "subwaytooter://oauth";
public TootApiResult request_sub( String path, Request.Builder request_builder ){ private TootApiResult request_sub( String path, Request.Builder request_builder ){
if( account == null ){ if( account == null ){
return new TootApiResult( "account is null" ); return new TootApiResult( "account is null" );
@ -217,7 +220,7 @@ public class TootApiClient {
+ "&grant_type=authorization_code" + "&grant_type=authorization_code"
// + "&username=" + Uri.encode( user_mail ) // + "&username=" + Uri.encode( user_mail )
// + "&password=" + Uri.encode( password ) // + "&password=" + Uri.encode( password )
+"&approval_prompt=force" + "&approval_prompt=force"
// +"&access_type=offline" // +"&access_type=offline"
; ;
// APIリクエストは失敗?する // APIリクエストは失敗?する
@ -227,7 +230,7 @@ public class TootApiClient {
} }
public TootApiResult authorize2( String url, String code ){ public TootApiResult authorize2( String code ){
JSONObject client_info = ClientInfo.load( instance ); JSONObject client_info = ClientInfo.load( instance );
if( client_info != null && client_info.optInt( KEY_AUTH_VERSION, 0 ) < AUTH_VERSION ){ if( client_info != null && client_info.optInt( KEY_AUTH_VERSION, 0 ) < AUTH_VERSION ){
@ -246,7 +249,7 @@ public class TootApiClient {
"grant_type=authorization_code" "grant_type=authorization_code"
+ "&code=" + Uri.encode( code ) + "&code=" + Uri.encode( code )
+ "&client_id=" + Uri.encode( Utils.optStringX( client_info, "client_id" ) ) + "&client_id=" + Uri.encode( Utils.optStringX( client_info, "client_id" ) )
+ "&redirect_uri=" + Uri.encode( REDIRECT_URL ) + "&redirect_uri=" + Uri.encode( REDIRECT_URL )
+ "&client_secret=" + Uri.encode( Utils.optStringX( client_info, "client_secret" ) ) + "&client_secret=" + Uri.encode( Utils.optStringX( client_info, "client_secret" ) )
+ "&scope=read write follow" + "&scope=read write follow"
+ "&scopes=read write follow"; + "&scopes=read write follow";

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/llColumnHeader"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:id="@+id/llColumnHeader"
android:background="@drawable/btn_bg_ddd" android:background="@drawable/btn_bg_ddd"
android:orientation="vertical"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
> >
<LinearLayout <LinearLayout
@ -52,8 +52,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="0dp" android:layout_marginTop="0dp"
android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"
> >
<TextView <TextView
@ -102,64 +102,127 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/llColumnSetting"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:background="?attr/colorColumnSettingBackground" android:background="?attr/colorColumnSettingBackground"
android:id="@+id/llColumnSetting" android:orientation="vertical"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
> >
<CheckBox <CheckBox
android:layout_width="match_parent" android:id="@+id/cbDontCloseColumn"
android:layout_height="wrap_content"
android:text="@string/with_attachment"
android:id="@+id/cbWithAttachment"
/>
<CheckBox
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/dont_close_column" android:text="@string/dont_close_column"
android:id="@+id/cbDontCloseColumn" />
<CheckBox
android:id="@+id/cbWithAttachment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/with_attachment"
/>
<CheckBox
android:id="@+id/cbDontShowBoost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_boost"
/>
<CheckBox
android:id="@+id/cbDontShowReply"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dont_show_reply"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/llRegexFilter"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/etRegexFilter"
android:text="@string/regex_filter"
android:textColor="?attr/colorColumnHeaderPageNumber"
/>
<TextView
android:id="@+id/tvRegexFilterError"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:textColor="?attr/colorRegexFilterError"
/>
</LinearLayout>
<EditText
android:id="@+id/etRegexFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:scrollHorizontally="true"
android:inputType="text"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/notification_delete"
android:id="@+id/btnDeleteNotification"
android:textAllCaps="false"
/> />
</LinearLayout> </LinearLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/llSearch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:background="?attr/colorSearchFormBackground" android:background="?attr/colorSearchFormBackground"
android:id="@+id/llSearch" android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
> >
<ImageButton <ImageButton
android:id="@+id/btnSearch"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:background="@drawable/btn_bg_transparent"
android:src="?attr/ic_search"
android:contentDescription="@string/search"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:id="@+id/btnSearch" android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/search"
android:src="?attr/ic_search"
/> />
<EditText <EditText
android:id="@+id/etSearch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_toStartOf="@id/btnSearch" android:layout_toStartOf="@id/btnSearch"
android:inputType="text"
android:id="@+id/etSearch"
android:imeOptions="actionSearch" android:imeOptions="actionSearch"
android:inputType="text"
tools:ignore="LabelFor"/> tools:ignore="LabelFor"/>
<CheckBox <CheckBox
android:id="@+id/cbResolve"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/etSearch" android:layout_below="@id/etSearch"
android:text="@string/resolve_non_local_account" android:text="@string/resolve_non_local_account"
android:id="@+id/cbResolve"
/> />
</RelativeLayout> </RelativeLayout>
@ -186,11 +249,11 @@
android:clipToPadding="false" android:clipToPadding="false"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:fastScrollEnabled="true"
android:paddingBottom="64dp" android:paddingBottom="64dp"
android:paddingEnd="12dp" android:paddingEnd="12dp"
android:paddingStart="12dp" android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:fastScrollEnabled="true"
/> />
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout> </com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>

View File

@ -170,8 +170,14 @@
<string name="send_header_content_warning">Content-Warning</string> <string name="send_header_content_warning">Content-Warning</string>
<string name="send_header_url">Status-URL</string> <string name="send_header_url">Status-URL</string>
<string name="column_has_dont_close_option">保護されたカラムは閉じられません</string> <string name="column_has_dont_close_option">保護されたカラムは閉じられません</string>
<string name="dont_close_column">保護(閉じることができない)</string> <string name="dont_close_column">保護(カラムを閉じない)</string>
<string name="theme_dark">暗い</string> <string name="theme_dark">暗い</string>
<string name="theme_light">明るい</string> <string name="theme_light">明るい</string>
<string name="ui_theme">UIテーマ(アプリ再起動が必要)</string> <string name="ui_theme">UIテーマ(アプリ再起動が必要)</string>
<string name="dont_show_boost">ブーストを表示しない</string>
<string name="dont_show_reply">返信を表示しない</string>
<string name="regex_error">正規表現エラー</string>
<string name="regex_filter">正規表現フィルタ(上級者向け)</string>
<string name="confirm_delete_notification">ユーザへの通知データがタンスのサーバから削除されます。よろしいですか?</string>
<string name="notification_delete">通知の削除</string>
</resources> </resources>

View File

@ -64,6 +64,9 @@
<!-- 設定画面のdividerの色 #ddd --> <!-- 設定画面のdividerの色 #ddd -->
<attr name="colorSettingDivider" format="color"/> <attr name="colorSettingDivider" format="color"/>
<!-- カラム設定の正規表現フィルタのエラー表示 #f00 -->
<attr name="colorRegexFilterError" format="color"/>
<attr name="btn_attachment" format="reference" /> <attr name="btn_attachment" format="reference" />
<attr name="btn_boost" format="reference" /> <attr name="btn_boost" format="reference" />

View File

@ -35,6 +35,7 @@
<color name="Light_colorShowMediaText">#fff</color> <color name="Light_colorShowMediaText">#fff</color>
<color name="Light_colorColumnListDeleteBackground">#FF0000</color> <color name="Light_colorColumnListDeleteBackground">#FF0000</color>
<color name="Light_colorColumnListDeleteText">#fff</color> <color name="Light_colorColumnListDeleteText">#fff</color>
<color name="Light_colorRegexFilterError">#f00</color>
<!-- Dark theme --> <!-- Dark theme -->
@ -75,5 +76,6 @@
<color name="Dark_colorShowMediaText">#ccFFFFFF</color> <color name="Dark_colorShowMediaText">#ccFFFFFF</color>
<color name="Dark_colorColumnListDeleteBackground">#FF0000</color> <color name="Dark_colorColumnListDeleteBackground">#FF0000</color>
<color name="Dark_colorColumnListDeleteText">#fff</color> <color name="Dark_colorColumnListDeleteText">#fff</color>
<color name="Dark_colorRegexFilterError">#f00</color>
</resources> </resources>

View File

@ -173,4 +173,10 @@
<string name="ui_theme">UI theme (app restart required)</string> <string name="ui_theme">UI theme (app restart required)</string>
<string name="theme_light">light</string> <string name="theme_light">light</string>
<string name="theme_dark">dark</string> <string name="theme_dark">dark</string>
<string name="dont_show_boost">dont show boost</string>
<string name="dont_show_reply">dont show reply</string>
<string name="regex_filter">regex filter</string>
<string name="regex_error">regex error</string>
<string name="notification_delete">delete notification</string>
<string name="confirm_delete_notification">your notification data on instance sarver will be deleted. Are you sure?</string>
</resources> </resources>

View File

@ -32,6 +32,9 @@
<item name="colorSearchFormBackground">@color/Light_colorSearchFormBackground</item> <item name="colorSearchFormBackground">@color/Light_colorSearchFormBackground</item>
<item name="colorSettingDivider">@color/Light_colorSettingDivider</item> <item name="colorSettingDivider">@color/Light_colorSettingDivider</item>
<item name="colorRegexFilterError">@color/Light_colorRegexFilterError</item>
<item name="btn_attachment">@drawable/btn_attachment</item> <item name="btn_attachment">@drawable/btn_attachment</item>
<item name="btn_boost">@drawable/btn_boost</item> <item name="btn_boost">@drawable/btn_boost</item>
<item name="btn_close">@drawable/btn_close</item> <item name="btn_close">@drawable/btn_close</item>
@ -105,6 +108,7 @@
<item name="colorColumnSettingBackground">@color/Dark_colorColumnSettingBackground</item> <item name="colorColumnSettingBackground">@color/Dark_colorColumnSettingBackground</item>
<item name="colorSearchFormBackground">@color/Dark_colorSearchFormBackground</item> <item name="colorSearchFormBackground">@color/Dark_colorSearchFormBackground</item>
<item name="colorSettingDivider">@color/Dark_colorSettingDivider</item> <item name="colorSettingDivider">@color/Dark_colorSettingDivider</item>
<item name="colorRegexFilterError">@color/Dark_colorRegexFilterError</item>
<item name="btn_attachment">@drawable/btn_attachment_dark</item> <item name="btn_attachment">@drawable/btn_attachment_dark</item>
<item name="btn_boost">@drawable/btn_boost_dark</item> <item name="btn_boost">@drawable/btn_boost_dark</item>

47
dumpFont.pl Normal file
View File

@ -0,0 +1,47 @@
#!perl --
use strict;
use warnings;
use Font::FreeType;
my $face = Font::FreeType->new->face('emojione_android.ttf');
my $f =0;
my $l =0;
my $n =0;
$face->foreach_char(sub{
my $codepoint = $_->char_code;
if( $codepoint < 80 ){
return;
}
if($n ==0 ){
if( $l == 0 ){
print "\tprivate static void initForFont",(++$f),"(){\n";
}
print "\t\taddFontCode(new int[]{";
}
printf "0x%x,",$codepoint;
if( ++$n >= 5 ){
$n =0;
print "});\n";
if( ++$l >= 100 ){
$l = 0;
print "\t}\n";
}
}
});
if( $n > 0 ){
print "});\n";
print "\t}\n";
}
print "\tstatic{\n";
for(my $i=1;$i<=$f;++$i){
print"\t\tinitForFont$i();\n";
}
print "\t}\n";