(試験実装)マストドン2.1のリスト機能に対応
|
@ -67,6 +67,7 @@ import jp.juggler.subwaytooter.api.TootApiClient;
|
|||
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount;
|
||||
import jp.juggler.subwaytooter.api.entity.TootApplication;
|
||||
import jp.juggler.subwaytooter.api.entity.TootList;
|
||||
import jp.juggler.subwaytooter.api.entity.TootNotification;
|
||||
import jp.juggler.subwaytooter.api.entity.TootRelationShip;
|
||||
import jp.juggler.subwaytooter.api.entity.TootResults;
|
||||
|
@ -110,7 +111,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
SharedPreferences pref;
|
||||
public Handler handler;
|
||||
AppState app_state;
|
||||
public AppState app_state;
|
||||
|
||||
// onActivityResultで設定されてonResumeで消化される
|
||||
// 状態保存の必要なし
|
||||
|
@ -731,6 +732,9 @@ public class ActMain extends AppCompatActivity
|
|||
}else if( id == R.id.nav_add_domain_blocks ){
|
||||
performAddTimeline( getDefaultInsertPosition(), false, Column.TYPE_DOMAIN_BLOCKS );
|
||||
|
||||
}else if( id == R.id.nav_add_list ){
|
||||
performAddTimeline( getDefaultInsertPosition(), false, Column.TYPE_LIST_LIST );
|
||||
|
||||
}else if( id == R.id.nav_follow_requests ){
|
||||
performAddTimeline( getDefaultInsertPosition(), false, Column.TYPE_FOLLOW_REQUESTS );
|
||||
|
||||
|
@ -784,8 +788,8 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
static final int COLUMN_WIDTH_MIN_DP = 300;
|
||||
|
||||
Typeface timeline_font;
|
||||
Typeface timeline_font_bold;
|
||||
public Typeface timeline_font;
|
||||
public Typeface timeline_font_bold;
|
||||
|
||||
boolean dont_crop_media_thumbnail;
|
||||
boolean mShortAcctLocalUser;
|
||||
|
@ -1813,6 +1817,7 @@ public class ActMain extends AppCompatActivity
|
|||
Utils.showToast( ActMain.this, false, R.string.app_was_muted );
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
interface FindAccountCallback {
|
||||
|
@ -3698,7 +3703,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
if( relation.muting ){
|
||||
for( Column column : app_state.column_list ){
|
||||
column.removeStatusByAccount( access_info, who.id );
|
||||
column.removeAccountInTimeline( access_info, who.id );
|
||||
}
|
||||
}else{
|
||||
for( Column column : app_state.column_list ){
|
||||
|
@ -3778,7 +3783,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
for( Column column : app_state.column_list ){
|
||||
if( relation.blocking ){
|
||||
column.removeStatusByAccount( access_info, who.id );
|
||||
column.removeAccountInTimeline( access_info, who.id );
|
||||
}else{
|
||||
column.removeFromBlockList( access_info, who.id );
|
||||
}
|
||||
|
@ -4278,7 +4283,7 @@ public class ActMain extends AppCompatActivity
|
|||
llColumnStrip.setColor( c );
|
||||
}
|
||||
|
||||
private ArrayList< SavedAccount > makeAccountList( @NonNull LogCategory log, boolean bAllowPseudo, @Nullable String pickup_host ){
|
||||
public ArrayList< SavedAccount > makeAccountList( @NonNull LogCategory log, boolean bAllowPseudo, @Nullable String pickup_host ){
|
||||
|
||||
ArrayList< SavedAccount > list_same_host = new ArrayList<>();
|
||||
ArrayList< SavedAccount > list_other_host = new ArrayList<>();
|
||||
|
@ -4871,4 +4876,169 @@ public class ActMain extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void createNewList( @NonNull final SavedAccount access_info, @NonNull final String title ){
|
||||
|
||||
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( access_info );
|
||||
|
||||
JSONObject content = new JSONObject( );
|
||||
try{
|
||||
content.put("title",title);
|
||||
}catch(Throwable ex){
|
||||
return new TootApiResult( Utils.formatError( ex,"can't encoding json parameter." ) );
|
||||
}
|
||||
|
||||
Request.Builder request_builder = new Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_JSON
|
||||
, content.toString()
|
||||
) );
|
||||
|
||||
|
||||
TootApiResult result = client.request( "/api/v1/lists" , request_builder );
|
||||
|
||||
if( result != null ){
|
||||
if( result.object != null ){
|
||||
list = TootList.parse( result.object );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TootList list;
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( list != null ){
|
||||
|
||||
for( Column column : app_state.column_list ){
|
||||
column.onListListUpdated( access_info );
|
||||
}
|
||||
|
||||
Utils.showToast(ActMain.this, false, R.string.list_created);
|
||||
|
||||
}else{
|
||||
Utils.showToast( ActMain.this, false, result.error );
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
public void callDeleteList( @NonNull final SavedAccount access_info, final long list_id ){
|
||||
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( access_info );
|
||||
|
||||
Request.Builder request_builder = new Request.Builder().delete();
|
||||
|
||||
TootApiResult result = client.request( "/api/v1/lists/"+ list_id , request_builder );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( result.object != null ){
|
||||
|
||||
for( Column column : app_state.column_list ){
|
||||
column.onListListUpdated( access_info );
|
||||
}
|
||||
|
||||
Utils.showToast(ActMain.this, false, R.string.delete_succeeded );
|
||||
|
||||
}else{
|
||||
Utils.showToast( ActMain.this, false, result.error );
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
public void callDeleteListMember( @NonNull final SavedAccount access_info, @NonNull final TootAccount who ,final long list_id ){
|
||||
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( access_info );
|
||||
|
||||
Request.Builder request_builder = new Request.Builder().delete();
|
||||
|
||||
TootApiResult result = client.request( "/api/v1/lists/"+list_id+"/accounts?account_ids[]="+ who.id , request_builder );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( result.object != null ){
|
||||
|
||||
for( Column column : app_state.column_list ){
|
||||
column.onListMemberUpdated( access_info ,list_id,who,false);
|
||||
}
|
||||
|
||||
Utils.showToast(ActMain.this, false, R.string.delete_succeeded );
|
||||
|
||||
}else{
|
||||
Utils.showToast( ActMain.this, false, result.error );
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -384,7 +384,7 @@ public class App1 extends Application {
|
|||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static AppState app_state;
|
||||
public static AppState app_state;
|
||||
|
||||
static AppState getAppState( @NonNull Context context ){
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import jp.juggler.subwaytooter.util.MyClickableSpan;
|
|||
import jp.juggler.subwaytooter.util.PostAttachment;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
class AppState {
|
||||
public class AppState {
|
||||
static final LogCategory log = new LogCategory( "AppState" );
|
||||
final Context context;
|
||||
final float density;
|
||||
|
@ -98,7 +98,7 @@ class AppState {
|
|||
}
|
||||
|
||||
private static final String FILE_COLUMN_LIST = "column_list";
|
||||
final ArrayList< Column > column_list = new ArrayList<>();
|
||||
public final ArrayList< Column > column_list = new ArrayList<>();
|
||||
|
||||
JSONArray encodeColumnList(){
|
||||
JSONArray array = new JSONArray();
|
||||
|
|
|
@ -32,6 +32,7 @@ import jp.juggler.subwaytooter.api.entity.TootContext;
|
|||
import jp.juggler.subwaytooter.api.entity.TootDomainBlock;
|
||||
import jp.juggler.subwaytooter.api.entity.TootGap;
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance;
|
||||
import jp.juggler.subwaytooter.api.entity.TootList;
|
||||
import jp.juggler.subwaytooter.api.entity.TootNotification;
|
||||
import jp.juggler.subwaytooter.api.entity.TootRelationShip;
|
||||
import jp.juggler.subwaytooter.api.entity.TootReport;
|
||||
|
@ -56,9 +57,10 @@ import jp.juggler.subwaytooter.view.MyListView;
|
|||
import jp.juggler.subwaytooter.util.ScrollPosition;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
@SuppressWarnings("WeakerAccess") class Column implements StreamReader.Callback {
|
||||
@SuppressWarnings("WeakerAccess") public class Column implements StreamReader.Callback {
|
||||
private static final LogCategory log = new LogCategory( "Column" );
|
||||
|
||||
|
||||
interface Callback {
|
||||
boolean isActivityStart();
|
||||
}
|
||||
|
@ -85,7 +87,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
return params[ idx ];
|
||||
}
|
||||
|
||||
private static final int READ_LIMIT = 80; // API側の上限が80です。ただし指定しても40しか返ってこないことが多い
|
||||
public static final int READ_LIMIT = 80; // API側の上限が80です。ただし指定しても40しか返ってこないことが多い
|
||||
private static final long LOOP_TIMEOUT = 10000L;
|
||||
private static final int LOOP_READ_ENOUGH = 30; // フィルタ後のデータ数がコレ以上ならループを諦めます
|
||||
private static final int RELATIONSHIP_LOAD_STEP = 40;
|
||||
|
@ -98,6 +100,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
private static final String PATH_FAVOURITES = "/api/v1/favourites?limit=" + READ_LIMIT;
|
||||
private static final String PATH_ACCOUNT_STATUSES = "/api/v1/accounts/%d/statuses?limit=" + READ_LIMIT; // 1:account_id
|
||||
private static final String PATH_HASHTAG = "/api/v1/timelines/tag/%s?limit=" + READ_LIMIT; // 1: hashtag(url encoded)
|
||||
private static final String PATH_LIST_TL = "/api/v1/timelines/list/%s?limit=" + READ_LIMIT;
|
||||
|
||||
// アカウントのリストを返すAPI
|
||||
private static final String PATH_ACCOUNT_FOLLOWING = "/api/v1/accounts/%d/following?limit=" + READ_LIMIT; // 1:account_id
|
||||
|
@ -107,11 +110,13 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
private static final String PATH_FOLLOW_REQUESTS = "/api/v1/follow_requests?limit=" + READ_LIMIT; // 1:account_id
|
||||
private static final String PATH_BOOSTED_BY = "/api/v1/statuses/%s/reblogged_by?limit=" + READ_LIMIT; // 1:status_id
|
||||
private static final String PATH_FAVOURITED_BY = "/api/v1/statuses/%s/favourited_by?limit=" + READ_LIMIT; // 1:status_id
|
||||
private static final String PATH_LIST_MEMBER = "/api/v1/lists/%s/accounts?limit=" + READ_LIMIT;
|
||||
|
||||
// 他のリストを返すAPI
|
||||
private static final String PATH_REPORTS = "/api/v1/reports?limit=" + READ_LIMIT;
|
||||
private static final String PATH_NOTIFICATIONS = "/api/v1/notifications?limit=" + READ_LIMIT;
|
||||
private static final String PATH_DOMAIN_BLOCK = "/api/v1/domain_blocks?limit=" + READ_LIMIT;
|
||||
private static final String PATH_LIST_LIST = "/api/v1/lists?limit=" + READ_LIMIT;
|
||||
|
||||
// リストではなくオブジェクトを返すAPI
|
||||
private static final String PATH_ACCOUNT = "/api/v1/accounts/%d"; // 1:account_id
|
||||
|
@ -119,6 +124,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
private static final String PATH_STATUSES_CONTEXT = "/api/v1/statuses/%d/context"; // 1:status_id
|
||||
static final String PATH_SEARCH = "/api/v1/search?q=%s"; // 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
||||
private static final String PATH_INSTANCE = "/api/v1/instance";
|
||||
private static final String PATH_LIST_INFO = "/api/v1/lists/%s";
|
||||
|
||||
static final String KEY_ACCOUNT_ROW_ID = "account_id";
|
||||
static final String KEY_TYPE = "type";
|
||||
|
@ -175,6 +181,9 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
static final int TYPE_DOMAIN_BLOCKS = 16;
|
||||
static final int TYPE_SEARCH_PORTAL = 17;
|
||||
static final int TYPE_INSTANCE_INFORMATION = 18;
|
||||
static final int TYPE_LIST_LIST = 19;
|
||||
static final int TYPE_LIST_TL = 20;
|
||||
static final int TYPE_LIST_MEMBER = 21;
|
||||
|
||||
@NonNull final Context context;
|
||||
@NonNull private final AppState app_state;
|
||||
|
@ -211,6 +220,8 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
static final int TAB_FOLLOWING = 1;
|
||||
static final int TAB_FOLLOWERS = 2;
|
||||
|
||||
volatile TootList list_info;
|
||||
|
||||
private long status_id;
|
||||
|
||||
private String hashtag;
|
||||
|
@ -241,6 +252,8 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
break;
|
||||
|
||||
case TYPE_PROFILE:
|
||||
case TYPE_LIST_TL:
|
||||
case TYPE_LIST_MEMBER:
|
||||
this.profile_id = (Long) getParamAt( params, 0 );
|
||||
break;
|
||||
|
||||
|
@ -299,6 +312,12 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
item.put( KEY_PROFILE_ID, profile_id );
|
||||
item.put( KEY_PROFILE_TAB, profile_tab );
|
||||
break;
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
case TYPE_LIST_TL:
|
||||
item.put( KEY_PROFILE_ID, profile_id );
|
||||
break;
|
||||
|
||||
case TYPE_HASHTAG:
|
||||
item.put( KEY_HASHTAG, hashtag );
|
||||
break;
|
||||
|
@ -329,7 +348,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
this.app_state = app_state;
|
||||
this.context = app_state.context;
|
||||
|
||||
long account_db_id = Utils.optLongX( src, KEY_ACCOUNT_ROW_ID );
|
||||
long account_db_id = Utils.optLongX( src, KEY_ACCOUNT_ROW_ID );
|
||||
if( account_db_id >= 0 ){
|
||||
SavedAccount ac = SavedAccount.loadAccount( context, log, account_db_id );
|
||||
if( ac == null ) throw new RuntimeException( "missing account" );
|
||||
|
@ -365,14 +384,19 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_CONVERSATION:
|
||||
case TYPE_BOOSTED_BY:
|
||||
case TYPE_FAVOURITED_BY:
|
||||
this.status_id = Utils.optLongX( src , KEY_STATUS_ID );
|
||||
this.status_id = Utils.optLongX( src, KEY_STATUS_ID );
|
||||
break;
|
||||
|
||||
case TYPE_PROFILE:
|
||||
this.profile_id = Utils.optLongX( src , KEY_PROFILE_ID );
|
||||
this.profile_id = Utils.optLongX( src, KEY_PROFILE_ID );
|
||||
this.profile_tab = src.optInt( KEY_PROFILE_TAB );
|
||||
break;
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
case TYPE_LIST_TL:
|
||||
this.profile_id = Utils.optLongX( src, KEY_PROFILE_ID );
|
||||
break;
|
||||
|
||||
case TYPE_HASHTAG:
|
||||
this.hashtag = src.optString( KEY_HASHTAG );
|
||||
break;
|
||||
|
@ -399,6 +423,8 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
return true;
|
||||
|
||||
case TYPE_PROFILE:
|
||||
case TYPE_LIST_TL:
|
||||
case TYPE_LIST_MEMBER:
|
||||
try{
|
||||
long who_id = (Long) getParamAt( params, 0 );
|
||||
return who_id == this.profile_id;
|
||||
|
@ -475,6 +501,16 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
, who_account != null ? AcctColor.getNickname( access_info.getFullAcct( who_account ) ) : Long.toString( profile_id )
|
||||
);
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
return context.getString( R.string.list_member_of
|
||||
, list_info != null ? list_info.title : Long.toString( profile_id )
|
||||
);
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
return context.getString( R.string.list_tl_of
|
||||
, list_info != null ? list_info.title : Long.toString( profile_id )
|
||||
);
|
||||
|
||||
case TYPE_CONVERSATION:
|
||||
return context.getString( R.string.conversation_around, status_id );
|
||||
|
||||
|
@ -563,6 +599,15 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
|
||||
case TYPE_FOLLOW_REQUESTS:
|
||||
return context.getString( R.string.follow_requests );
|
||||
|
||||
case TYPE_LIST_LIST:
|
||||
return context.getString( R.string.lists );
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
return context.getString( R.string.list_member );
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
return context.getString( R.string.list_timeline );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,6 +672,15 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
|
||||
case TYPE_FOLLOW_REQUESTS:
|
||||
return R.attr.ic_account_add;
|
||||
|
||||
case TYPE_LIST_LIST:
|
||||
return R.attr.ic_list2;
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
return R.attr.ic_list2;
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
return R.attr.ic_list2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -673,7 +727,8 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
}
|
||||
|
||||
// ミュート、ブロックが成功した時に呼ばれる
|
||||
void removeStatusByAccount( SavedAccount target_account, long who_id ){
|
||||
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
|
||||
void removeAccountInTimeline( SavedAccount target_account, long who_id ){
|
||||
if( ! target_account.acct.equals( access_info.acct ) ) return;
|
||||
|
||||
ArrayList< Object > tmp_list = new ArrayList<>( list_data.size() );
|
||||
|
@ -685,8 +740,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if( o instanceof TootNotification ){
|
||||
}else if( o instanceof TootNotification ){
|
||||
TootNotification item = (TootNotification) o;
|
||||
if( item.account.id == who_id ) continue;
|
||||
if( item.status != null ){
|
||||
|
@ -695,6 +749,9 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
if( item.status.reblog != null && item.status.reblog.account != null && item.status.reblog.account.id == who_id )
|
||||
continue;
|
||||
}
|
||||
}else if( o instanceof TootAccount ){
|
||||
TootAccount item = (TootAccount) o;
|
||||
if( item.id == who_id ) continue;
|
||||
}
|
||||
|
||||
tmp_list.add( o );
|
||||
|
@ -922,6 +979,27 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
|
||||
}
|
||||
|
||||
public void onListListUpdated( @NonNull SavedAccount account ){
|
||||
if( column_type == TYPE_LIST_LIST && access_info.acct.equals( account.acct ) ){
|
||||
startLoading();
|
||||
ColumnViewHolder vh = getViewHolder();
|
||||
if( vh != null ) vh.onListListUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
public void onListMemberUpdated( SavedAccount account, long list_id, TootAccount who, boolean bAdd ){
|
||||
if( column_type == TYPE_LIST_TL && access_info.acct.equals( account.acct ) && list_id == profile_id ){
|
||||
if( ! bAdd ){
|
||||
removeAccountInTimeline( account, who.id );
|
||||
}
|
||||
}else if( column_type == TYPE_LIST_MEMBER && access_info.acct.equals( account.acct ) && list_id == profile_id ){
|
||||
if( ! bAdd ){
|
||||
removeAccountInTimeline( account, who.id );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// カラムを閉じた後のnotifyDataSetChangedのタイミングで、add/removeされる順序が期待通りにならないので
|
||||
|
@ -1151,13 +1229,13 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
}
|
||||
}
|
||||
|
||||
// @Nullable String parseMaxId( TootApiResult result ){
|
||||
// if( result != null && result.link_older != null ){
|
||||
// Matcher m = reMaxId.matcher( result.link_older );
|
||||
// if( m.find() ) return m.group( 1 );
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// @Nullable String parseMaxId( TootApiResult result ){
|
||||
// if( result != null && result.link_older != null ){
|
||||
// Matcher m = reMaxId.matcher( result.link_older );
|
||||
// if( m.find() ) return m.group( 1 );
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
@NonNull static final VersionString version_1_6 = new VersionString( "1.6" );
|
||||
|
||||
|
@ -1189,6 +1267,13 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
}
|
||||
}
|
||||
|
||||
void parseListInfo( TootApiClient client, String path_base ){
|
||||
TootApiResult result = client.request( path_base );
|
||||
if( result != null && result.object != null ){
|
||||
Column.this.list_info = TootList.parse( result.object );
|
||||
}
|
||||
}
|
||||
|
||||
TootInstance instance_tmp;
|
||||
|
||||
TootApiResult getInstanceInformation( @NonNull TootApiClient client, @Nullable String instance_name ){
|
||||
|
@ -1355,6 +1440,16 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
return result;
|
||||
}
|
||||
|
||||
TootApiResult parseListList( TootApiClient client, String path_base ){
|
||||
TootApiResult result = client.request( path_base );
|
||||
if( result != null ){
|
||||
saveRange( result, true, true );
|
||||
list_tmp = new ArrayList<>();
|
||||
list_tmp.addAll( TootList.parseList( result.array ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TootApiResult parseNotifications( TootApiClient client, String path_base ){
|
||||
|
||||
long time_start = SystemClock.elapsedRealtime();
|
||||
|
@ -1504,6 +1599,19 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_DOMAIN_BLOCKS:
|
||||
return parseDomainList( client, PATH_DOMAIN_BLOCK );
|
||||
|
||||
case TYPE_LIST_LIST:
|
||||
return parseListList( client, PATH_LIST_LIST );
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
parseListInfo( client, String.format( Locale.JAPAN, PATH_LIST_INFO, profile_id ) );
|
||||
client.callback.publishApiProgress( "" ); // カラムヘッダの表示を更新
|
||||
return getStatuses( client, String.format( Locale.JAPAN, PATH_LIST_TL, profile_id ) );
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
parseListInfo( client, String.format( Locale.JAPAN, PATH_LIST_INFO, profile_id ) );
|
||||
client.callback.publishApiProgress( "" ); // カラムヘッダの表示を更新
|
||||
return parseAccountList( client, String.format( Locale.JAPAN, PATH_LIST_MEMBER, profile_id ) );
|
||||
|
||||
case TYPE_FOLLOW_REQUESTS:
|
||||
return parseAccountList( client, PATH_FOLLOW_REQUESTS );
|
||||
|
||||
|
@ -1693,7 +1801,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
private static final Pattern reMaxId = Pattern.compile( "[&?]max_id=(\\d+)" ); // より古いデータの取得に使う
|
||||
public static final Pattern reMaxId = Pattern.compile( "[&?]max_id=(\\d+)" ); // より古いデータの取得に使う
|
||||
private static final Pattern reSinceId = Pattern.compile( "[&?]since_id=(\\d+)" ); // より新しいデータの取得に使う
|
||||
|
||||
private String max_id;
|
||||
|
@ -1702,9 +1810,13 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
|
||||
private void saveRange( TootApiResult result, boolean bBottom, boolean bTop ){
|
||||
if( result != null ){
|
||||
if( bBottom && result.link_older != null ){
|
||||
Matcher m = reMaxId.matcher( result.link_older );
|
||||
if( m.find() ) max_id = m.group( 1 );
|
||||
if( bBottom ){
|
||||
if( result.link_older == null ){
|
||||
max_id = null;
|
||||
}else{
|
||||
Matcher m = reMaxId.matcher( result.link_older );
|
||||
if( m.find() ) max_id = m.group( 1 );
|
||||
}
|
||||
}
|
||||
if( bTop && result.link_newer != null ){
|
||||
Matcher m = reSinceId.matcher( result.link_newer );
|
||||
|
@ -1715,7 +1827,9 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
|
||||
private boolean saveRangeEnd( TootApiResult result ){
|
||||
if( result != null ){
|
||||
if( result.link_older != null ){
|
||||
if( result.link_older == null ){
|
||||
max_id = null;
|
||||
}else{
|
||||
Matcher m = reMaxId.matcher( result.link_older );
|
||||
if( m.find() ){
|
||||
max_id = m.group( 1 );
|
||||
|
@ -1964,6 +2078,14 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
}
|
||||
}
|
||||
|
||||
void parseListInfo( TootApiClient client, String path_base ){
|
||||
TootApiResult result = client.request( path_base );
|
||||
if( result != null && result.object != null ){
|
||||
Column.this.list_info = TootList.parse( result.object );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TootApiResult getAccountList( TootApiClient client, String path_base ){
|
||||
long time_start = SystemClock.elapsedRealtime();
|
||||
char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
|
||||
|
@ -2067,6 +2189,61 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
return result;
|
||||
}
|
||||
|
||||
TootApiResult getListList( TootApiClient client, String path_base ){
|
||||
long time_start = SystemClock.elapsedRealtime();
|
||||
char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
|
||||
String last_since_id = since_id;
|
||||
TootApiResult result = client.request( addRange( bBottom, path_base ) );
|
||||
if( result != null && result.array != null ){
|
||||
saveRange( result, bBottom, ! bBottom );
|
||||
list_tmp = new ArrayList<>();
|
||||
TootList.List src = TootList.parseList( result.array );
|
||||
list_tmp.addAll( src );
|
||||
if( ! bBottom ){
|
||||
for( ; ; ){
|
||||
if( isCancelled() ){
|
||||
log.d( "refresh-list-top: cancelled." );
|
||||
break;
|
||||
}
|
||||
|
||||
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
||||
// 直前のデータが0個なら終了とみなすしかなさそう
|
||||
if( src.isEmpty() ){
|
||||
log.d( "refresh-list-top: previous size == 0." );
|
||||
break;
|
||||
}
|
||||
|
||||
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
||||
String max_id = Long.toString( src.get( src.size() - 1 ).id );
|
||||
|
||||
if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
|
||||
log.d( "refresh-list-top: timeout. make gap." );
|
||||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
TootGap gap = new TootGap( max_id, last_since_id );
|
||||
list_tmp.add( gap );
|
||||
break;
|
||||
}
|
||||
|
||||
String path = path_base + delimiter + "max_id=" + max_id + "&since_id=" + last_since_id;
|
||||
TootApiResult result2 = client.request( path );
|
||||
if( result2 == null || result2.array == null ){
|
||||
log.d( "refresh-list-top: timeout. error or retry. make gap." );
|
||||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
TootGap gap = new TootGap( max_id, last_since_id );
|
||||
list_tmp.add( gap );
|
||||
break;
|
||||
}
|
||||
|
||||
src = TootList.parseList( result2.array );
|
||||
list_tmp.addAll( src );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TootApiResult getReportList( TootApiClient client, String path_base ){
|
||||
long time_start = SystemClock.elapsedRealtime();
|
||||
char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
|
||||
|
@ -2432,6 +2609,24 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
String.format( Locale.JAPAN, PATH_ACCOUNT_FOLLOWERS, profile_id ) );
|
||||
|
||||
}
|
||||
|
||||
case TYPE_LIST_LIST:
|
||||
return getListList( client, PATH_LIST_LIST );
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
if( list_info == null ){
|
||||
parseListInfo( client, String.format( Locale.JAPAN, PATH_LIST_INFO, profile_id ) );
|
||||
client.callback.publishApiProgress( "" );
|
||||
}
|
||||
return getStatusList( client, String.format( Locale.JAPAN, PATH_LIST_TL, profile_id ) );
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
if( list_info == null ){
|
||||
parseListInfo( client, String.format( Locale.JAPAN, PATH_LIST_INFO, profile_id ) );
|
||||
client.callback.publishApiProgress( "" );
|
||||
}
|
||||
return getAccountList( client, String.format( Locale.JAPAN, PATH_LIST_MEMBER, profile_id ) );
|
||||
|
||||
case TYPE_MUTES:
|
||||
return getAccountList( client, PATH_MUTES );
|
||||
|
||||
|
@ -2848,6 +3043,9 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_FEDERATE:
|
||||
return getStatusList( client, PATH_FEDERATE );
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
return getStatusList( client, String.format( Locale.JAPAN, PATH_LIST_TL, profile_id ) );
|
||||
|
||||
case TYPE_FAVOURITES:
|
||||
return getStatusList( client, PATH_FAVOURITES );
|
||||
|
||||
|
@ -3004,7 +3202,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
private void setItemTop( @NonNull ColumnViewHolder holder, int idx, int y ){
|
||||
|
||||
MyListView listView = holder.getListView();
|
||||
boolean hasHeader = holder.hasHeaderView();
|
||||
boolean hasHeader = holder.getHeaderView() != null;
|
||||
if( hasHeader ){
|
||||
// Adapter中から見たpositionとListViewから見たpositionにズレができる
|
||||
idx = idx + 1;
|
||||
|
@ -3021,7 +3219,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
private int getItemTop( @NonNull ColumnViewHolder holder, int idx ){
|
||||
|
||||
MyListView listView = holder.getListView();
|
||||
boolean hasHeader = holder.hasHeaderView();
|
||||
boolean hasHeader = holder.getHeaderView() != null;
|
||||
|
||||
if( hasHeader ){
|
||||
// Adapter中から見たpositionとListViewから見たpositionにズレができる
|
||||
|
@ -3220,10 +3418,19 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_HASHTAG:
|
||||
app_state.stream_reader.unregister(
|
||||
access_info
|
||||
, StreamReader.EP_HASHTAG + "?tag=" + Uri.encode( hashtag )
|
||||
, StreamReader.EP_HASHTAG + Uri.encode( hashtag )
|
||||
, this
|
||||
);
|
||||
break;
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
app_state.stream_reader.unregister(
|
||||
access_info
|
||||
, StreamReader.EP_LIST_TL + Long.toString( profile_id )
|
||||
, this
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3314,18 +3521,16 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_CONVERSATION:
|
||||
return true;
|
||||
}
|
||||
// static final int TYPE_FAVOURITES = 5;
|
||||
// static final int TYPE_REPORTS = 6;
|
||||
// static final int TYPE_MUTES = 11;
|
||||
// static final int TYPE_BLOCKS = 12;
|
||||
// static final int TYPE_FOLLOW_REQUESTS = 13;
|
||||
// static final int TYPE_BOOSTED_BY = 14;
|
||||
// static final int TYPE_FAVOURITED_BY = 15;
|
||||
// static final int TYPE_DOMAIN_BLOCKS = 16;
|
||||
// static final int TYPE_FAVOURITES = 5;
|
||||
// static final int TYPE_REPORTS = 6;
|
||||
// static final int TYPE_MUTES = 11;
|
||||
// static final int TYPE_BLOCKS = 12;
|
||||
// static final int TYPE_FOLLOW_REQUESTS = 13;
|
||||
// static final int TYPE_BOOSTED_BY = 14;
|
||||
// static final int TYPE_FAVOURITED_BY = 15;
|
||||
// static final int TYPE_DOMAIN_BLOCKS = 16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
boolean canStreaming(){
|
||||
switch( column_type ){
|
||||
default:
|
||||
|
@ -3336,6 +3541,7 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_LOCAL:
|
||||
case TYPE_FEDERATE:
|
||||
case TYPE_HASHTAG:
|
||||
case TYPE_LIST_TL:
|
||||
return ! access_info.isPseudo();
|
||||
}
|
||||
}
|
||||
|
@ -3426,10 +3632,41 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
case TYPE_HASHTAG:
|
||||
app_state.stream_reader.register(
|
||||
access_info
|
||||
, StreamReader.EP_HASHTAG + "&tag=" + Uri.encode( hashtag )
|
||||
, StreamReader.EP_HASHTAG + Uri.encode( hashtag )
|
||||
, this
|
||||
);
|
||||
break;
|
||||
|
||||
case TYPE_LIST_TL:
|
||||
app_state.stream_reader.register(
|
||||
access_info
|
||||
, StreamReader.EP_LIST_TL + Long.toString( profile_id )
|
||||
, this
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull String getListTitle(){
|
||||
switch(column_type){
|
||||
default:
|
||||
return "?";
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
case TYPE_LIST_TL:
|
||||
String sv = list_info == null ? null : list_info.title;
|
||||
return !TextUtils.isEmpty( sv ) ? sv : Long.toString( profile_id );
|
||||
}
|
||||
}
|
||||
public long getListId(){
|
||||
switch(column_type){
|
||||
default:
|
||||
return -1L;
|
||||
|
||||
case TYPE_LIST_MEMBER:
|
||||
case TYPE_LIST_TL:
|
||||
return profile_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
@ -83,6 +84,11 @@ class ColumnViewHolder
|
|||
private final View llRegexFilter;
|
||||
private final Button btnDeleteNotification;
|
||||
|
||||
private final View llListList;
|
||||
private final EditText etListName;
|
||||
private final View btnListAdd;
|
||||
|
||||
|
||||
ColumnViewHolder( ActMain arg_activity, View root ){
|
||||
this.activity = arg_activity;
|
||||
|
||||
|
@ -124,6 +130,22 @@ class ColumnViewHolder
|
|||
cbResolve = root.findViewById( R.id.cbResolve );
|
||||
|
||||
llSearch = root.findViewById( R.id.llSearch );
|
||||
llListList = root.findViewById( R.id.llListList );
|
||||
|
||||
btnListAdd= root.findViewById( R.id.btnListAdd );
|
||||
etListName= root.findViewById( R.id.etListName );
|
||||
btnListAdd.setOnClickListener( this );
|
||||
|
||||
etListName.setOnEditorActionListener( new TextView.OnEditorActionListener() {
|
||||
@Override public boolean onEditorAction( TextView v, int actionId, KeyEvent event ){
|
||||
boolean handled = false;
|
||||
if( actionId == EditorInfo.IME_ACTION_SEND ){
|
||||
btnListAdd.performClick();
|
||||
handled = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
} );
|
||||
|
||||
llColumnSetting = root.findViewById( R.id.llColumnSetting );
|
||||
|
||||
|
@ -261,6 +283,7 @@ class ColumnViewHolder
|
|||
|
||||
case Column.TYPE_INSTANCE_INFORMATION:
|
||||
status_adapter.header = new HeaderViewHolderInstance(activity, column, listView );
|
||||
break;
|
||||
}
|
||||
|
||||
// 添付メディアや正規表現のフィルタ
|
||||
|
@ -343,6 +366,7 @@ class ColumnViewHolder
|
|||
|
||||
vg( btnDeleteNotification, column.column_type == Column.TYPE_NOTIFICATIONS );
|
||||
vg( llSearch, ( column.column_type == Column.TYPE_SEARCH || column.column_type == Column.TYPE_SEARCH_PORTAL ) );
|
||||
vg( llListList, ( column.column_type == Column.TYPE_LIST_LIST ) );
|
||||
vg( cbResolve, ( column.column_type == Column.TYPE_SEARCH ) );
|
||||
|
||||
// tvRegexFilterErrorの表示を更新
|
||||
|
@ -468,7 +492,7 @@ class ColumnViewHolder
|
|||
}
|
||||
}
|
||||
|
||||
private void loadBackgroundImage( final ImageView iv, final String url ){
|
||||
@SuppressLint("StaticFieldLeak") private void loadBackgroundImage( final ImageView iv, final String url ){
|
||||
try{
|
||||
if( TextUtils.isEmpty( url ) ){
|
||||
// 指定がないなら閉じる
|
||||
|
@ -571,6 +595,10 @@ class ColumnViewHolder
|
|||
llColumnSetting.setVisibility( View.GONE );
|
||||
}
|
||||
|
||||
void onListListUpdated(){
|
||||
etListName.setText( "" );
|
||||
}
|
||||
|
||||
@Override public void onRefresh( SwipyRefreshLayoutDirection direction ){
|
||||
if( column == null ) return;
|
||||
|
||||
|
@ -714,6 +742,15 @@ class ColumnViewHolder
|
|||
int idx = activity.app_state.column_list.indexOf( column );
|
||||
ActColumnCustomize.open( activity, idx, ActMain.REQUEST_CODE_COLUMN_COLOR );
|
||||
break;
|
||||
|
||||
case R.id.btnListAdd:
|
||||
String tv = etListName.getText().toString().trim();
|
||||
if( TextUtils.isEmpty( tv ) ){
|
||||
Utils.showToast( activity, true, R.string.list_name_empty );
|
||||
return;
|
||||
}
|
||||
activity.createNewList( column.access_info, tv );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -737,8 +774,8 @@ class ColumnViewHolder
|
|||
/////////////////////////////////////////////////////////////////
|
||||
// Column から呼ばれる
|
||||
|
||||
boolean hasHeaderView(){
|
||||
return status_adapter != null && status_adapter.header != null;
|
||||
@Nullable HeaderViewHolderBase getHeaderView(){
|
||||
return status_adapter == null ? null : status_adapter.header;
|
||||
}
|
||||
|
||||
SwipyRefreshLayout getRefreshLayout(){
|
||||
|
|
|
@ -20,6 +20,8 @@ import jp.juggler.subwaytooter.api.entity.TootAccount;
|
|||
import jp.juggler.subwaytooter.api.entity.TootNotification;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm;
|
||||
import jp.juggler.subwaytooter.dialog.DlgListMemberAdd;
|
||||
import jp.juggler.subwaytooter.dialog.DlgQRCode;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.table.UserRelation;
|
||||
|
@ -40,8 +42,6 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
private final Dialog dialog;
|
||||
|
||||
private final ArrayList< SavedAccount > account_list_non_pseudo = new ArrayList<>();
|
||||
|
||||
DlgContextMenu(
|
||||
@NonNull ActMain activity
|
||||
, @NonNull Column column
|
||||
|
@ -102,6 +102,9 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
View btnHideBoost = viewRoot.findViewById( R.id.btnHideBoost );
|
||||
View btnShowBoost = viewRoot.findViewById( R.id.btnShowBoost );
|
||||
|
||||
View btnListMemberAdd= viewRoot.findViewById( R.id.btnListMemberAdd );
|
||||
View btnListMemberRemove= viewRoot.findViewById( R.id.btnListMemberRemove );
|
||||
|
||||
btnStatusWebPage.setOnClickListener( this );
|
||||
btnText.setOnClickListener( this );
|
||||
btnFavouriteAnotherAccount.setOnClickListener( this );
|
||||
|
@ -129,6 +132,8 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
btnConversationMute.setOnClickListener( this );
|
||||
btnHideBoost.setOnClickListener( this );
|
||||
btnShowBoost.setOnClickListener( this );
|
||||
btnListMemberAdd.setOnClickListener( this );
|
||||
btnListMemberRemove.setOnClickListener( this );
|
||||
|
||||
viewRoot.findViewById( R.id.btnQuoteUrlStatus ).setOnClickListener( this );
|
||||
viewRoot.findViewById( R.id.btnQuoteUrlAccount ).setOnClickListener( this );
|
||||
|
@ -137,6 +142,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
final ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( activity, log );
|
||||
// final ArrayList< SavedAccount > account_list_non_pseudo_same_instance = new ArrayList<>();
|
||||
|
||||
ArrayList< SavedAccount > account_list_non_pseudo = new ArrayList<>();
|
||||
for( SavedAccount a : account_list ){
|
||||
if( ! a.isPseudo() ){
|
||||
account_list_non_pseudo.add( a );
|
||||
|
@ -300,6 +306,16 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
btnOpenTimeline.setText( activity.getString( R.string.open_local_timeline_for, host ) );
|
||||
}
|
||||
|
||||
if( access_info.isPseudo() ){
|
||||
btnListMemberAdd.setVisibility( View.VISIBLE );
|
||||
btnListMemberRemove.setVisibility( View.GONE );
|
||||
}else{
|
||||
btnListMemberAdd.setVisibility( View.VISIBLE );
|
||||
btnListMemberRemove.setVisibility(
|
||||
column.column_type == Column.TYPE_LIST_MEMBER || column.column_type == Column.TYPE_LIST_TL
|
||||
? View.VISIBLE : View.GONE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void show(){
|
||||
|
@ -619,7 +635,24 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
activity.callFollowingReblogs( access_info, who, true );
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case R.id.btnListMemberAdd:
|
||||
if( who != null ){
|
||||
new DlgListMemberAdd( activity,who, access_info,column.getListId() ).show();
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.btnListMemberRemove:
|
||||
if( who != null ){
|
||||
final long list_id = column.getListId();
|
||||
String list_title = column.getListTitle();
|
||||
DlgConfirm.openSimple( activity, activity.getString( R.string.list_member_delete_confirm, access_info.getFullAcct( who ), list_title ), new Runnable() {
|
||||
@Override public void run(){
|
||||
activity.callDeleteListMember( access_info, who ,list_id );
|
||||
}
|
||||
} );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
|
@ -28,10 +27,13 @@ import jp.juggler.subwaytooter.api.entity.TootAttachment;
|
|||
import jp.juggler.subwaytooter.api.entity.TootCard;
|
||||
import jp.juggler.subwaytooter.api.entity.TootDomainBlock;
|
||||
import jp.juggler.subwaytooter.api.entity.TootGap;
|
||||
import jp.juggler.subwaytooter.api.entity.TootList;
|
||||
import jp.juggler.subwaytooter.api.entity.TootNotification;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.api_msp.entity.MSPToot;
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog;
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm;
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.table.ContentWarning;
|
||||
import jp.juggler.subwaytooter.table.MediaShown;
|
||||
|
@ -42,8 +44,6 @@ import jp.juggler.subwaytooter.util.EmojiImageSpan;
|
|||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator;
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiSpan;
|
||||
import jp.juggler.subwaytooter.view.MyEditText;
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
|
||||
import jp.juggler.subwaytooter.view.MyListView;
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView;
|
||||
|
@ -98,6 +98,10 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
private final View llSearchTag;
|
||||
private final Button btnSearchTag;
|
||||
|
||||
private final View llList;
|
||||
private final Button btnListTL;
|
||||
private final View btnListMore;
|
||||
|
||||
private final LinearLayout llExtra;
|
||||
|
||||
@Nullable private final TextView tvApplication;
|
||||
|
@ -110,6 +114,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
@Nullable private TootGap gap;
|
||||
@Nullable private TootDomainBlock domain_block;
|
||||
@Nullable private TootNotification notification;
|
||||
@Nullable private TootList list;
|
||||
|
||||
private final boolean bSimpleList;
|
||||
|
||||
|
@ -204,6 +209,14 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
this.btnSearchTag = view.findViewById( R.id.btnSearchTag );
|
||||
this.tvApplication = view.findViewById( R.id.tvApplication );
|
||||
|
||||
this. llList= view.findViewById( R.id.llList );
|
||||
this. btnListTL= view.findViewById( R.id.btnListTL );
|
||||
this. btnListMore= view.findViewById( R.id.btnListMore );
|
||||
|
||||
btnListTL.setOnClickListener( this );
|
||||
btnListMore.setOnClickListener( this );
|
||||
|
||||
|
||||
btnSearchTag.setOnClickListener( this );
|
||||
btnContentWarning.setOnClickListener( this );
|
||||
btnShowMedia.setOnClickListener( this );
|
||||
|
@ -251,7 +264,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
if( tvApplication != null ){
|
||||
tvApplication.setTextSize( activity.timeline_font_size_sp );
|
||||
}
|
||||
|
||||
btnListTL.setTextSize( activity.timeline_font_size_sp );
|
||||
}
|
||||
|
||||
if( ! Float.isNaN( activity.acct_font_size_sp ) ){
|
||||
|
@ -284,11 +297,13 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
this.gap = null;
|
||||
this.domain_block = null;
|
||||
this.notification = null;
|
||||
this.list = null;
|
||||
|
||||
llBoosted.setVisibility( View.GONE );
|
||||
llFollow.setVisibility( View.GONE );
|
||||
llStatus.setVisibility( View.GONE );
|
||||
llSearchTag.setVisibility( View.GONE );
|
||||
llList.setVisibility( View.GONE );
|
||||
llExtra.removeAllViews();
|
||||
|
||||
if( item == null ) return;
|
||||
|
@ -379,9 +394,17 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
showGap( (TootGap) item );
|
||||
}else if( item instanceof TootDomainBlock ){
|
||||
showDomainBlock( (TootDomainBlock) item );
|
||||
}else if( item instanceof TootList ){
|
||||
showList( (TootList) item );
|
||||
}
|
||||
}
|
||||
|
||||
private void showList( TootList list ){
|
||||
this.list = list;
|
||||
llList.setVisibility( View.VISIBLE );
|
||||
btnListTL.setText( list.title );
|
||||
}
|
||||
|
||||
private void showDomainBlock( @NonNull TootDomainBlock domain_block ){
|
||||
this.gap = null;
|
||||
this.domain_block = domain_block;
|
||||
|
@ -728,7 +751,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
@Override public void onClick( View v ){
|
||||
|
||||
int pos = activity.nextPosition( column );
|
||||
final int pos = activity.nextPosition( column );
|
||||
|
||||
switch( v.getId() ){
|
||||
case R.id.btnHideMedia:
|
||||
|
@ -816,7 +839,38 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
.show();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case R.id.btnListTL:
|
||||
if( list != null ){
|
||||
activity.addColumn( pos,access_info,Column.TYPE_LIST_TL,list.id);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.btnListMore:
|
||||
if( list != null ){
|
||||
ActionsDialog ad = new ActionsDialog();
|
||||
ad.addAction( activity.getString( R.string.list_timeline ), new Runnable() {
|
||||
@Override public void run(){
|
||||
activity.addColumn( pos,access_info,Column.TYPE_LIST_TL,list.id);
|
||||
}
|
||||
} );
|
||||
ad.addAction( activity.getString( R.string.list_member ), new Runnable() {
|
||||
@Override public void run(){
|
||||
activity.addColumn( pos,access_info,Column.TYPE_LIST_MEMBER,list.id);
|
||||
}
|
||||
} );
|
||||
ad.addAction( activity.getString( R.string.delete ), new Runnable() {
|
||||
@Override public void run(){
|
||||
DlgConfirm.openSimple( activity, activity.getString( R.string.list_delete_confirm, list.title ), new Runnable() {
|
||||
@Override public void run(){
|
||||
activity.callDeleteList( access_info, list.id );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
ad.show( activity ,list.title );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ import okhttp3.WebSocketListener;
|
|||
static final String EP_USER = "/api/v1/streaming/?stream=user";
|
||||
static final String EP_PUBLIC = "/api/v1/streaming/?stream=public";
|
||||
static final String EP_PUBLIC_LOCAL = "/api/v1/streaming/?stream=public:local";
|
||||
static final String EP_HASHTAG = "/api/v1/streaming/?stream=hashtag"; // + &tag=hashtag (先頭の#を含まない)
|
||||
static final String EP_HASHTAG = "/api/v1/streaming/?stream=hashtag&tag="; // + hashtag (先頭の#を含まない)
|
||||
static final String EP_LIST_TL = "/api/v1/streaming/?stream=list&list="; // + list_id
|
||||
|
||||
static final Pattern reNumber = Pattern.compile( "([-]?\\d+)" );
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.text.Spannable;
|
|||
import android.text.TextUtils;
|
||||
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions;
|
||||
import jp.juggler.subwaytooter.util.EmojiDecoder;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
@ -189,6 +188,16 @@ public class TootAccount {
|
|||
}
|
||||
|
||||
private static final Pattern reWhitespace = Pattern.compile( "[\\s\\t\\x0d\\x0a]+" );
|
||||
|
||||
|
||||
public Spannable decodeDisplayName( Context context ){
|
||||
|
||||
// remove white spaces
|
||||
String sv = reWhitespace.matcher( display_name ).replaceAll( " " );
|
||||
|
||||
// decode emoji code
|
||||
return new DecodeOptions().setProfileEmojis( profile_emojis ).decodeEmoji( context, sv );
|
||||
}
|
||||
|
||||
public void setDisplayName( Context context, String username, String sv ){
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
|
@ -196,12 +205,7 @@ public class TootAccount {
|
|||
}else{
|
||||
this.display_name = Utils.sanitizeBDI( sv );
|
||||
}
|
||||
|
||||
// remove white spaces
|
||||
sv = reWhitespace.matcher( this.display_name ).replaceAll( " " );
|
||||
|
||||
// decode emoji code
|
||||
this.decoded_display_name = new DecodeOptions().setProfileEmojis( this.profile_emojis ).decodeEmoji( context, sv );
|
||||
this.decoded_display_name = decodeDisplayName(context);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package jp.juggler.subwaytooter.api.entity;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
public class TootList {
|
||||
private static final LogCategory log = new LogCategory( "TootList" );
|
||||
|
||||
public long id;
|
||||
|
||||
@Nullable public String title;
|
||||
|
||||
@Nullable
|
||||
public static TootList parse( JSONObject src ){
|
||||
if( src == null ) return null;
|
||||
try{
|
||||
TootList dst = new TootList();
|
||||
dst.id = Utils.optLongX( src, "id" );
|
||||
dst.title = Utils.optStringX( src, "title" );
|
||||
|
||||
return dst;
|
||||
}catch( Throwable ex ){
|
||||
log.trace( ex );
|
||||
log.e( ex, "parse failed." );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class List extends ArrayList< TootList > {
|
||||
}
|
||||
|
||||
@NonNull public static List parseList( JSONArray array ){
|
||||
TootList.List result = new TootList.List();
|
||||
if( array != null ){
|
||||
int array_size = array.length();
|
||||
result.ensureCapacity( array_size );
|
||||
for( int i = 0 ; i < array_size ; ++ i ){
|
||||
JSONObject obj = array.optJSONObject( i );
|
||||
if( obj != null ) {
|
||||
TootList dst = TootList.parse( obj );
|
||||
if( dst != null ) result.add( dst );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package jp.juggler.subwaytooter.dialog;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
@ -21,9 +22,9 @@ public class DlgConfirm {
|
|||
}
|
||||
|
||||
public static void open(
|
||||
final Activity activity
|
||||
, String message
|
||||
, final Callback callback
|
||||
@NonNull final Activity activity
|
||||
, @NonNull String message
|
||||
, @NonNull final Callback callback
|
||||
){
|
||||
|
||||
if( ! callback.isConfirmEnabled() ){
|
||||
|
@ -52,5 +53,28 @@ public class DlgConfirm {
|
|||
.show();
|
||||
}
|
||||
|
||||
public static void openSimple(@NonNull final Activity activity
|
||||
, @NonNull String message
|
||||
, @NonNull final Runnable callback
|
||||
){
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
final View view = activity.getLayoutInflater().inflate( R.layout.dlg_confirm, null, false );
|
||||
final TextView tvMessage = view.findViewById( R.id.tvMessage );
|
||||
final CheckBox cbSkipNext = view.findViewById( R.id.cbSkipNext );
|
||||
tvMessage.setText( message );
|
||||
cbSkipNext.setVisibility( View.GONE );
|
||||
|
||||
new AlertDialog.Builder( activity )
|
||||
.setView( view )
|
||||
.setCancelable( true )
|
||||
.setNegativeButton( R.string.cancel, null )
|
||||
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override public void onClick( DialogInterface dialog, int which ){
|
||||
callback.run();
|
||||
}
|
||||
} )
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,624 @@
|
|||
package jp.juggler.subwaytooter.dialog;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.ActMain;
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.Column;
|
||||
import jp.juggler.subwaytooter.R;
|
||||
import jp.juggler.subwaytooter.Styler;
|
||||
import jp.juggler.subwaytooter.api.TootApiClient;
|
||||
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount;
|
||||
import jp.juggler.subwaytooter.api.entity.TootList;
|
||||
import jp.juggler.subwaytooter.api.entity.TootRelationShip;
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
public class DlgListMemberAdd implements View.OnClickListener {
|
||||
|
||||
private static final LogCategory log = new LogCategory( "DlgListMemberAdd" );
|
||||
|
||||
@NonNull private final ActMain activity;
|
||||
|
||||
// @NonNull private final TootAccount target_user;
|
||||
@NonNull private final String target_user_full_acct;
|
||||
|
||||
private SavedAccount list_owner;
|
||||
private long list_id;
|
||||
|
||||
@NonNull private final Dialog dialog;
|
||||
private final Button btnListOwner;
|
||||
private final Button btnList;
|
||||
|
||||
@NonNull private final ArrayList< SavedAccount > account_list;
|
||||
@Nullable private ArrayList< TootList > list_list;
|
||||
|
||||
public DlgListMemberAdd( @NonNull ActMain _activity, @NonNull TootAccount who, @NonNull SavedAccount _list_owner, long list_id ){
|
||||
this.activity = _activity;
|
||||
// this.target_user = who;
|
||||
this.target_user_full_acct = _list_owner.getFullAcct( who );
|
||||
this.account_list = activity.makeAccountList( log, false, null );
|
||||
|
||||
if( _list_owner.isPseudo() ){
|
||||
this.list_owner = null;
|
||||
this.list_id = - 1L;
|
||||
}else{
|
||||
this.list_owner = _list_owner;
|
||||
this.list_id = list_id; // -1Lならリスト無し
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams") final View view = activity.getLayoutInflater().inflate( R.layout.dlg_list_member_add, null, false );
|
||||
|
||||
MyNetworkImageView ivUser = view.findViewById( R.id.ivUser );
|
||||
TextView tvUserName = view.findViewById( R.id.tvUserName );
|
||||
TextView tvUserAcct = view.findViewById( R.id.tvUserAcct );
|
||||
btnListOwner = view.findViewById( R.id.btnListOwner );
|
||||
btnList = view.findViewById( R.id.btnList );
|
||||
|
||||
view.findViewById( R.id.btnCancel ).setOnClickListener( this );
|
||||
view.findViewById( R.id.btnOk ).setOnClickListener( this );
|
||||
|
||||
ivUser.setImageUrl( App1.pref, 16f, who.avatar_static, who.avatar );
|
||||
|
||||
NetworkEmojiInvalidator user_name_invalidator = new NetworkEmojiInvalidator( activity.handler, tvUserName );
|
||||
Spannable name = who.decodeDisplayName( activity );
|
||||
tvUserName.setText( name );
|
||||
user_name_invalidator.register( name );
|
||||
|
||||
tvUserAcct.setText( target_user_full_acct );
|
||||
|
||||
btnListOwner.setOnClickListener( this );
|
||||
btnList.setOnClickListener( this );
|
||||
|
||||
setListOwner( list_owner, list_id );
|
||||
|
||||
this.dialog = new Dialog( activity );
|
||||
dialog.setContentView( view );
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void show(){
|
||||
//noinspection ConstantConditions
|
||||
dialog.getWindow().setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT );
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override public void onClick( View v ){
|
||||
switch( v.getId() ){
|
||||
case R.id.btnListOwner:
|
||||
AccountPicker.pick( activity, false, false, null, account_list, new AccountPicker.AccountPickerCallback() {
|
||||
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
||||
// アカウントが変更された時だけリストIDを変更する
|
||||
long new_list_id = ( list_owner != null && ai.acct.equals( list_owner.acct ) ? list_id : - 1L );
|
||||
setListOwner( ai, new_list_id );
|
||||
}
|
||||
} );
|
||||
break;
|
||||
case R.id.btnList:
|
||||
openListPicker();
|
||||
break;
|
||||
case R.id.btnCancel:
|
||||
try{
|
||||
dialog.cancel();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
break;
|
||||
case R.id.btnOk:
|
||||
addListMember(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setListOwner( @Nullable SavedAccount a, long new_list_id ){
|
||||
this.list_owner = a;
|
||||
if( a == null ){
|
||||
btnListOwner.setText( R.string.not_selected );
|
||||
btnListOwner.setTextColor( Styler.getAttributeColor( activity, android.R.attr.textColorPrimary ) );
|
||||
btnListOwner.setBackgroundResource( R.drawable.btn_bg_transparent );
|
||||
//
|
||||
|
||||
}else{
|
||||
String acct = a.getFullAcct( a );
|
||||
AcctColor ac = AcctColor.load( acct );
|
||||
String nickname = AcctColor.hasNickname( ac ) ? ac.nickname : acct;
|
||||
btnListOwner.setText( nickname );
|
||||
|
||||
if( AcctColor.hasColorBackground( ac ) ){
|
||||
btnListOwner.setBackgroundColor( ac.color_bg );
|
||||
}else{
|
||||
btnListOwner.setBackgroundResource( R.drawable.btn_bg_transparent );
|
||||
}
|
||||
if( AcctColor.hasColorForeground( ac ) ){
|
||||
btnListOwner.setTextColor( ac.color_fg );
|
||||
}else{
|
||||
btnListOwner.setTextColor( Styler.getAttributeColor( activity, android.R.attr.textColorPrimary ) );
|
||||
}
|
||||
}
|
||||
|
||||
loadLists( new_list_id );
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void loadLists( final long new_list_id ){
|
||||
|
||||
if( list_owner == null ){
|
||||
showList( null, - 1L );
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection deprecation
|
||||
final ProgressDialog progress = new ProgressDialog( activity );
|
||||
|
||||
final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
|
||||
|
||||
ArrayList< TootList > list_list = new ArrayList<>();
|
||||
|
||||
void showProgress( final String sv ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setMessage( sv );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override protected TootApiResult doInBackground( Void... params ){
|
||||
|
||||
TootApiClient client = new TootApiClient( activity, new TootApiClient.Callback() {
|
||||
@Override public boolean isApiCancelled(){
|
||||
return isCancelled();
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( final String sv ){
|
||||
showProgress( sv );
|
||||
}
|
||||
} );
|
||||
|
||||
client.setAccount( list_owner );
|
||||
String path_base = "/api/v1/lists?limit=" + Column.READ_LIMIT;
|
||||
|
||||
// head
|
||||
TootApiResult result = client.request( path_base );
|
||||
if( result == null || result.array == null ){
|
||||
list_list = null;
|
||||
return result;
|
||||
}
|
||||
showProgress( activity.getString( R.string.parsing_response ) );
|
||||
list_list.addAll( TootList.parseList( result.array ) );
|
||||
String max_id;
|
||||
if( result.link_older == null ){
|
||||
max_id = null;
|
||||
}else{
|
||||
Matcher m = Column.reMaxId.matcher( result.link_older );
|
||||
max_id = m.find() ? m.group( 1 ) : null;
|
||||
}
|
||||
|
||||
// trail
|
||||
while( max_id != null ){
|
||||
result = client.request( path_base + "&max_id=" + max_id );
|
||||
if( result == null || result.array == null ){
|
||||
list_list = null;
|
||||
return result;
|
||||
}
|
||||
showProgress( activity.getString( R.string.parsing_response ) );
|
||||
list_list.addAll( TootList.parseList( result.array ) );
|
||||
if( result.link_older == null ){
|
||||
max_id = null;
|
||||
}else{
|
||||
Matcher m = Column.reMaxId.matcher( result.link_older );
|
||||
max_id = m.find() ? m.group( 1 ) : null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
try{
|
||||
progress.dismiss();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
|
||||
showList( list_list, new_list_id );
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result != null
|
||||
&& ! TextUtils.isEmpty( result.error )
|
||||
&& !(result.response !=null && result.response.code() ==404 )
|
||||
){
|
||||
Utils.showToast( activity, true, result.error );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( false );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
|
||||
}
|
||||
|
||||
private void showList( @Nullable ArrayList< TootList > _list, long new_list_id ){
|
||||
this.list_list = _list;
|
||||
|
||||
if( list_list == null ){
|
||||
list_id = - 1L;
|
||||
btnList.setText( R.string.cant_access_list );
|
||||
return;
|
||||
}
|
||||
|
||||
for( TootList l : list_list ){
|
||||
if( l.id == new_list_id ){
|
||||
this.list_id = new_list_id;
|
||||
btnList.setText( l.title );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.list_id = - 1L;
|
||||
btnList.setText( R.string.not_selected );
|
||||
|
||||
}
|
||||
|
||||
private void openListPicker(){
|
||||
if( list_list == null ) return;
|
||||
|
||||
ActionsDialog ad = new ActionsDialog();
|
||||
ad.addAction( activity.getString( R.string.list_create ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openListCreator();
|
||||
}
|
||||
} );
|
||||
for( TootList l : list_list ){
|
||||
final long list_id = l.id;
|
||||
ad.addAction( ! TextUtils.isEmpty( l.title ) ? l.title : Long.toString( list_id ), new Runnable() {
|
||||
@Override public void run(){
|
||||
showList( list_list, list_id );
|
||||
}
|
||||
} );
|
||||
}
|
||||
ad.show( activity, null );
|
||||
}
|
||||
|
||||
private void openListCreator(){
|
||||
DlgTextInput.show( activity, activity.getString( R.string.list_create ), null, new DlgTextInput.Callback() {
|
||||
@Override public void onEmptyError(){
|
||||
Utils.showToast( activity, false, R.string.list_name_empty );
|
||||
}
|
||||
|
||||
@Override public void onOK( final Dialog dialog, final String title ){
|
||||
|
||||
//noinspection deprecation
|
||||
final ProgressDialog progress = new ProgressDialog( activity );
|
||||
|
||||
@SuppressLint("StaticFieldLeak") final AsyncTask< Void, String, TootApiResult > task
|
||||
= new AsyncTask< Void, String, TootApiResult >() {
|
||||
|
||||
void showProgress( final String sv ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setMessage( sv );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override protected TootApiResult doInBackground( Void... params ){
|
||||
TootApiClient client = new TootApiClient( activity, new TootApiClient.Callback() {
|
||||
@Override public boolean isApiCancelled(){
|
||||
return isCancelled();
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( String s ){
|
||||
}
|
||||
} );
|
||||
|
||||
client.setAccount( list_owner );
|
||||
|
||||
JSONObject content = new JSONObject();
|
||||
try{
|
||||
content.put( "title", title );
|
||||
}catch( Throwable ex ){
|
||||
return new TootApiResult( Utils.formatError( ex, "can't encoding json parameter." ) );
|
||||
}
|
||||
|
||||
Request.Builder request_builder = new Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_JSON
|
||||
, content.toString()
|
||||
) );
|
||||
|
||||
TootApiResult result = client.request( "/api/v1/lists", request_builder );
|
||||
showProgress( activity.getString( R.string.parsing_response ) );
|
||||
if( result != null ){
|
||||
if( result.object != null ){
|
||||
list = TootList.parse( result.object );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TootList list;
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
|
||||
try{
|
||||
progress.dismiss();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( list != null ){
|
||||
for( Column column : activity.app_state.column_list ){
|
||||
column.onListListUpdated( list_owner );
|
||||
}
|
||||
|
||||
Utils.showToast( activity, false, R.string.list_created );
|
||||
|
||||
loadLists( list.id );
|
||||
|
||||
try{
|
||||
dialog.dismiss();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
}else{
|
||||
Utils.showToast( activity, true, result.error );
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( false );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
static final Pattern reFollowError = Pattern.compile( "follow",Pattern.CASE_INSENSITIVE );
|
||||
|
||||
private void addListMember(final boolean bFollow){
|
||||
if( list_owner == null || list_id == - 1L ){
|
||||
Utils.showToast( activity, false, R.string.list_not_selected );
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection deprecation
|
||||
final ProgressDialog progress = new ProgressDialog( activity );
|
||||
|
||||
@SuppressLint("StaticFieldLeak") final AsyncTask< Void, String, TootApiResult > task
|
||||
= new AsyncTask< Void, String, TootApiResult >() {
|
||||
|
||||
void showProgress( final String sv ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setMessage( sv );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override protected TootApiResult doInBackground( Void... params ){
|
||||
TootApiClient client = new TootApiClient( activity, new TootApiClient.Callback() {
|
||||
@Override public boolean isApiCancelled(){
|
||||
return isCancelled();
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( String s ){
|
||||
showProgress(s);
|
||||
}
|
||||
} );
|
||||
|
||||
TootApiResult result;
|
||||
|
||||
client.setAccount( list_owner );
|
||||
|
||||
// リストに追加したいアカウントの自タンスでのアカウントIDを取得する
|
||||
String path = "/api/v1/accounts/search?q=" + target_user_full_acct;
|
||||
result = client.request( path );
|
||||
if( result ==null || result.array ==null ){
|
||||
return result;
|
||||
}
|
||||
|
||||
for( int i = 0, ie = result.array.length() ; i < ie ; ++ i ){
|
||||
TootAccount item = TootAccount.parse( activity, list_owner, result.array.optJSONObject( i ) );
|
||||
if( list_owner.getFullAcct( item ).equals( target_user_full_acct ) ){
|
||||
local_who = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( local_who == null ){
|
||||
return new TootApiResult( activity.getString( R.string.account_sync_failed ) );
|
||||
}
|
||||
|
||||
if( bFollow ){
|
||||
TootRelationShip relation;
|
||||
if( list_owner.isLocalUser( local_who ) ){
|
||||
Request.Builder request_builder = new Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
|
||||
, "" // 空データ
|
||||
) );
|
||||
|
||||
result = client.request( "/api/v1/accounts/"+local_who.id+"/follow", request_builder );
|
||||
}else{
|
||||
// リモートフォローする
|
||||
Request.Builder request_builder = new Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
|
||||
, "uri=" + Uri.encode( local_who.acct )
|
||||
) );
|
||||
|
||||
result = client.request( "/api/v1/follows", request_builder );
|
||||
if( result == null || result.object == null ) return result;
|
||||
|
||||
TootAccount a = TootAccount.parse( activity, list_owner, result.object );
|
||||
if( a == null ){
|
||||
return new TootApiResult( "parse error." );
|
||||
}
|
||||
|
||||
result = client.request( "/api/v1/accounts/relationships?id[]=" + a.id );
|
||||
}
|
||||
if( result == null || result.array == null ) return result;
|
||||
TootRelationShip.List relation_list = TootRelationShip.parseList( result.array );
|
||||
relation = relation_list.isEmpty() ? null : relation_list.get(0);
|
||||
|
||||
if( relation == null ){
|
||||
return new TootApiResult( "parse error.");
|
||||
}else if( ! relation.following ){
|
||||
if( relation.requested ){
|
||||
return new TootApiResult( activity.getString( R.string.cant_add_list_follow_requesting ) );
|
||||
}else{
|
||||
// リモートフォローの場合、正常ケースでもここを通る場合がある
|
||||
// 何もしてはいけない…
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject content = new JSONObject();
|
||||
try{
|
||||
JSONArray account_ids = new JSONArray();
|
||||
account_ids.put( Long.toString( local_who.id ) );
|
||||
content.put( "account_ids", account_ids );
|
||||
}catch( Throwable ex ){
|
||||
return new TootApiResult( Utils.formatError( ex, "can't encoding json parameter." ) );
|
||||
}
|
||||
|
||||
Request.Builder request_builder = new Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_JSON
|
||||
, content.toString()
|
||||
) );
|
||||
|
||||
return client.request( "/api/v1/lists/" + list_id + "/accounts", request_builder );
|
||||
|
||||
}
|
||||
|
||||
TootAccount local_who;
|
||||
|
||||
@Override
|
||||
protected void onCancelled( TootApiResult result ){
|
||||
onPostExecute( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
|
||||
try{
|
||||
progress.dismiss();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( result.object != null ){
|
||||
for( Column column : activity.app_state.column_list ){
|
||||
column.onListMemberUpdated( list_owner, list_id, local_who, true );
|
||||
}
|
||||
|
||||
Utils.showToast( activity, false, R.string.list_member_added );
|
||||
|
||||
try{
|
||||
dialog.dismiss();
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
}else{
|
||||
|
||||
if( result.response != null
|
||||
&& result.response.code() == 422
|
||||
&& result.error != null && reFollowError.matcher( result.error ).find()
|
||||
){
|
||||
|
||||
if( !bFollow ){
|
||||
DlgConfirm.openSimple(
|
||||
activity
|
||||
, activity.getString( R.string.list_retry_with_follow, target_user_full_acct )
|
||||
, new Runnable() {
|
||||
@Override public void run(){
|
||||
addListMember( true );
|
||||
}
|
||||
}
|
||||
);
|
||||
}else{
|
||||
new AlertDialog.Builder( activity )
|
||||
.setCancelable( true )
|
||||
.setMessage( R.string.cant_add_list_while_follow_progress )
|
||||
.setNeutralButton( R.string.close,null )
|
||||
.show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.showToast( activity, true, result.error );
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( false );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
}
|
|
@ -862,7 +862,7 @@ public class Utils {
|
|||
}catch( Throwable ex ){
|
||||
log.trace( ex );
|
||||
}
|
||||
return sb.toString();
|
||||
return sb.toString().replaceAll( "\n+","\n" );
|
||||
}
|
||||
|
||||
public interface ScanViewCallback {
|
||||
|
|
After Width: | Height: | Size: 246 B |
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 279 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 176 B |
After Width: | Height: | Size: 152 B |
After Width: | Height: | Size: 204 B |
After Width: | Height: | Size: 186 B |
After Width: | Height: | Size: 220 B |
After Width: | Height: | Size: 197 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 525 B |
|
@ -500,6 +500,38 @@
|
|||
android:textAllCaps="false"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnListMemberAdd"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="32dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:text="@string/list_member_add"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnListMemberRemove"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="32dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:text="@string/list_member_remove"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSendMessage"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/member_will_be_added_to_list"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/setting_row_form"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
|
||||
<jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
android:id="@+id/ivUser"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/thumbnail"
|
||||
android:scaleType="fitEnd"
|
||||
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUserName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Follower Name"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvUserAcct"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:textColor="?attr/colorTimeSmall"
|
||||
android:textSize="12sp"
|
||||
tools:text="aaaaaaaaaaaaaaaa"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/list_owner"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/setting_row_form"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnListOwner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/list"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/setting_row_form"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnCancel"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/cancel"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOk"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/ok"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -493,4 +493,31 @@
|
|||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnListTL"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
<ImageButton
|
||||
android:id="@+id/btnListMore"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:src="?attr/btn_more"
|
||||
android:contentDescription="@string/more"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -391,4 +391,34 @@
|
|||
android:textAllCaps="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnListTL"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:textAllCaps="false"
|
||||
/>
|
||||
<ImageButton
|
||||
android:id="@+id/btnListMore"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:src="?attr/btn_more"
|
||||
android:contentDescription="@string/more"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -303,6 +303,42 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/llListList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSearchFormBackground"
|
||||
android:paddingBottom="3dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="3dp"
|
||||
>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnListAdd"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:contentDescription="@string/search"
|
||||
android:src="?attr/ic_add"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etListName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@id/btnListAdd"
|
||||
android:imeOptions="actionSend"
|
||||
android:inputType="text"
|
||||
android:hint="@string/list_create_hint"
|
||||
tools:ignore="LabelFor"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/flColumnBackground"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -36,11 +36,18 @@
|
|||
android:id="@+id/nav_add_tl_local"
|
||||
android:icon="?attr/btn_local_tl"
|
||||
android:title="@string/local_timeline"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_add_tl_federate"
|
||||
android:icon="?attr/btn_federate_tl"
|
||||
android:title="@string/federate_timeline"/>
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_add_list"
|
||||
android:icon="?attr/ic_list2"
|
||||
android:title="@string/lists"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_add_tl_search"
|
||||
android:icon="?attr/ic_search"
|
||||
|
|
|
@ -529,6 +529,35 @@
|
|||
<string name="hide_boost_in_home">Hide boosts from this user to your home</string>
|
||||
<string name="show_boost_in_home">Show boosts from this user to your home</string>
|
||||
<string name="operation_succeeded">Operation succeeded.</string>
|
||||
|
||||
<string name="list_member_add">Add user to List…</string>
|
||||
<string name="list_member_remove">Remove user to List…</string>
|
||||
<string name="lists">Lists</string>
|
||||
<string name="list_show_member">show list users</string>
|
||||
<string name="list_show_timeline">show list TL</string>
|
||||
<string name="list_member_of">\"%1$s\" list users</string>
|
||||
<string name="list_tl_of">\"%1$s\" List TL</string>
|
||||
<string name="list_create_hint">name of new list</string>
|
||||
<string name="list_name_empty">please input name of new list.</string>
|
||||
<string name="list_created">new list created.</string>
|
||||
<string name="list_delete_confirm">\"%1$s\" list will be deleted. Are you sure?</string>
|
||||
<string name="list_member">List users</string>
|
||||
<string name="list_timeline">List timeline</string>
|
||||
<string name="list_member_delete_confirm">\"%1$s\" will be deleted from \"%2$s\". Are you sure?</string>
|
||||
<string name="member_will_be_added_to_list">The user will be added to list</string>
|
||||
<string name="list_owner">List owner</string>
|
||||
<string name="list_create">(Create new list…)</string>
|
||||
<string name="list">List</string>
|
||||
<string name="list_not_supported">this instance does not support list.</string>
|
||||
<string name="cant_access_list">Can\'t access to lists.</string>
|
||||
<string name="list_not_selected">please select list.</string>
|
||||
<string name="account_sync_failed">account sync failed.</string>
|
||||
<string name="list_retry_with_follow">You have not followed %1$s yet. Would you like to follow and then add to list?</string>
|
||||
<string name="cant_add_list_while_follow_progress">Can\'t add user to list:\nfollow operation is progress on server side.</string>
|
||||
<string name="cant_add_list_follow_requesting">Can\'t add user to list:\nPlease wait until the follow request is approved.</string>
|
||||
<string name="cant_add_list_follow_failed">Can\'t add user to list:\nfollow operation failed.</string>
|
||||
<string name="list_member_added">User has been added to the list.</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_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
|
||||
|
|
|
@ -816,4 +816,32 @@
|
|||
<string name="hide_boost_in_home">このユーザのブーストをホームに表示しない</string>
|
||||
<string name="show_boost_in_home">このユーザのブーストをホームに表示する</string>
|
||||
<string name="operation_succeeded">変更できました</string>
|
||||
|
||||
<string name="list_member_add">リストに追加…</string>
|
||||
<string name="list_member_remove">リストから削除…</string>
|
||||
<string name="lists">リストの一覧</string>
|
||||
<string name="list_show_member">リストのユーザ</string>
|
||||
<string name="list_show_timeline">リストのタイムライン</string>
|
||||
<string name="list_member_of">\"%1$s\"リストのユーザ</string>
|
||||
<string name="list_tl_of">\"%1$s\"リストのタイムライン</string>
|
||||
<string name="list_create_hint">新しいリストの名前</string>
|
||||
<string name="list_name_empty">新しいリストの名前を入力してください</string>
|
||||
<string name="list_created">リストを作成しました</string>
|
||||
<string name="list_delete_confirm">\"%1$s\"リストは削除されます。よろしいですか?</string>
|
||||
<string name="list_member">リストのユーザ</string>
|
||||
<string name="list_timeline">リストのタイムライン</string>
|
||||
<string name="list_member_delete_confirm">\"%1$s\"は\"%2$s\"から削除されます。よろしいですか?</string>
|
||||
<string name="member_will_be_added_to_list">リストに追加されるユーザ</string>
|
||||
<string name="list_owner">リスト所有者</string>
|
||||
<string name="list_create">(リストを作る…)</string>
|
||||
<string name="list">リスト</string>
|
||||
<string name="list_not_supported">このタンスはリスト機能をサポートしていません</string>
|
||||
<string name="cant_access_list">リストにアクセスできません</string>
|
||||
<string name="list_not_selected">リストを選択してください</string>
|
||||
<string name="account_sync_failed">アカウントを同期できません</string>
|
||||
<string name="list_retry_with_follow">あなたは %1$s をまだフォローしていません。フォローしてからリストに追加しますか?</string>
|
||||
<string name="cant_add_list_while_follow_progress">リストに追加できません:\nフォロー処理がサーバで進行中です</string>
|
||||
<string name="cant_add_list_follow_requesting">リストに追加できません:\nフォローリクエストが承認されるのを待ってください</string>
|
||||
<string name="cant_add_list_follow_failed">リストに追加できません:\nフォロー操作に失敗しました</string>
|
||||
<string name="list_member_added">追加できました</string>
|
||||
</resources>
|
||||
|
|
|
@ -121,5 +121,7 @@
|
|||
<attr name="ic_eye_off" format="reference" />
|
||||
<attr name="ic_pin" format="reference" />
|
||||
<attr name="ic_follow_wait" format="reference" />
|
||||
<attr name="ic_list2" format="reference" />
|
||||
<attr name="ic_add" format="reference" />
|
||||
|
||||
</resources>
|
|
@ -521,4 +521,31 @@
|
|||
<string name="show_boost_in_home">Show boosts from this user to your home</string>
|
||||
<string name="operation_succeeded">Operation succeeded.</string>
|
||||
|
||||
<string name="list_member_add">Add user to List…</string>
|
||||
<string name="list_member_remove">Remove user to List…</string>
|
||||
<string name="lists">Lists</string>
|
||||
<string name="list_show_member">show list users</string>
|
||||
<string name="list_show_timeline">show list TL</string>
|
||||
<string name="list_member_of">\"%1$s\" list users</string>
|
||||
<string name="list_tl_of">\"%1$s\" List TL</string>
|
||||
<string name="list_create_hint">name of new list</string>
|
||||
<string name="list_name_empty">please input name of new list.</string>
|
||||
<string name="list_created">new list created.</string>
|
||||
<string name="list_delete_confirm">\"%1$s\" list will be deleted. Are you sure?</string>
|
||||
<string name="list_member">List users</string>
|
||||
<string name="list_timeline">List timeline</string>
|
||||
<string name="list_member_delete_confirm">\"%1$s\" will be deleted from \"%2$s\". Are you sure?</string>
|
||||
<string name="member_will_be_added_to_list">The user will be added to list</string>
|
||||
<string name="list_owner">List owner</string>
|
||||
<string name="list_create">(Create new list…)</string>
|
||||
<string name="list">List</string>
|
||||
<string name="list_not_supported">this instance does not support list.</string>
|
||||
<string name="cant_access_list">Can\'t access to lists</string>
|
||||
<string name="list_not_selected">please select list.</string>
|
||||
<string name="account_sync_failed">account sync failed.</string>
|
||||
<string name="list_retry_with_follow">You have not followed %1$s yet. Would you like to follow and then add to list?</string>
|
||||
<string name="cant_add_list_while_follow_progress">Can\'t add user to list:\nfollow operation is progress on server side.</string>
|
||||
<string name="cant_add_list_follow_requesting">Can\'t add user to list:\nPlease wait until the follow request is approved.</string>
|
||||
<string name="cant_add_list_follow_failed">Can\'t add user to list:\nfollow operation failed.</string>
|
||||
<string name="list_member_added">User has been added to the list.</string>
|
||||
</resources>
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
<item name="ic_eye_off">@drawable/ic_eye_off</item>
|
||||
<item name="ic_pin">@drawable/ic_pin</item>
|
||||
<item name="ic_follow_wait">@drawable/ic_follow_wait</item>
|
||||
<item name="ic_list2">@drawable/ic_list2</item>
|
||||
<item name="ic_add">@drawable/ic_add</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
@ -186,6 +188,8 @@
|
|||
<item name="ic_eye_off">@drawable/ic_eye_off_dark</item>
|
||||
<item name="ic_pin">@drawable/ic_pin_dark</item>
|
||||
<item name="ic_follow_wait">@drawable/ic_follow_wait_dark</item>
|
||||
<item name="ic_list2">@drawable/ic_list2_dark</item>
|
||||
<item name="ic_add">@drawable/ic_add_dark</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
|