フォロー/アンフォロー/ミュート/アンミュート/ブロック/アンブロック/レポート/プロフ画面からのメンション

This commit is contained in:
tateisu 2017-04-24 00:16:05 +09:00
parent 4f3effe3d8
commit c0ab3e5a25
11 changed files with 754 additions and 157 deletions

View File

@ -14,6 +14,8 @@
<w>swipy</w>
<w>timelines</w>
<w>unfavourite</w>
<w>unfollow</w>
<w>unmute</w>
<w>unreblog</w>
</words>
</dictionary>

View File

@ -41,6 +41,7 @@ import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.dialog.AccountPicker;
import jp.juggler.subwaytooter.dialog.LoginForm;
import jp.juggler.subwaytooter.dialog.ReportForm;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
@ -53,7 +54,7 @@ public class ActMain extends AppCompatActivity
public static final LogCategory log = new LogCategory( "ActMain" );
static boolean update_at_resume = false;
// @Override
// protected void attachBaseContext(Context newBase) {
// super.attachBaseContext( CalligraphyContextWrapper.wrap(newBase));
@ -66,7 +67,7 @@ public class ActMain extends AppCompatActivity
super.onCreate( savedInstanceState );
requestWindowFeature( Window.FEATURE_NO_TITLE );
pref = Pref.pref(this);
pref = Pref.pref( this );
initUI();
loadColumnList();
@ -101,12 +102,12 @@ public class ActMain extends AppCompatActivity
}
}
if(update_at_resume){
if( update_at_resume ){
update_at_resume = false;
// TODO: 各カラムを更新する
}
if( pager_adapter.getCount() == 0){
if( pager_adapter.getCount() == 0 ){
llEmpty.setVisibility( View.VISIBLE );
}
}
@ -118,12 +119,12 @@ public class ActMain extends AppCompatActivity
super.onPause();
}
static final int REQUEST_CODE_COLUMN_LIST =1;
static final int REQUEST_CODE_COLUMN_LIST = 1;
boolean isOrderChanged(ArrayList<Integer> new_order){
boolean isOrderChanged( ArrayList< Integer > new_order ){
if( new_order.size() != pager_adapter.getCount() ) return true;
for(int i=0,ie = new_order.size();i<ie;++i ){
if( new_order.get(i) != i ) return true;
for( int i = 0, ie = new_order.size() ; i < ie ; ++ i ){
if( new_order.get( i ) != i ) return true;
}
return false;
}
@ -132,8 +133,8 @@ public class ActMain extends AppCompatActivity
protected void onActivityResult( int requestCode, int resultCode, Intent data ){
if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_COLUMN_LIST ){
if( data != null ){
ArrayList<Integer> order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
if( order != null && isOrderChanged(order) ){
ArrayList< Integer > order = data.getIntegerArrayListExtra( ActColumnList.EXTRA_ORDER );
if( order != null && isOrderChanged( order ) ){
pager_adapter.setOrder( pager, order );
}
@ -155,10 +156,10 @@ public class ActMain extends AppCompatActivity
DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
if( drawer.isDrawerOpen( GravityCompat.START ) ){
drawer.closeDrawer( GravityCompat.START );
}else if( pref.getBoolean( Pref.KEY_BACK_TO_COLUMN_LIST ,false) ){
}else if( pref.getBoolean( Pref.KEY_BACK_TO_COLUMN_LIST, false ) ){
performColumnList();
}else if( ! pager_adapter.column_list.isEmpty() ){
performColumnClose( false,pager_adapter.getColumn( pager.getCurrentItem() ) );
performColumnClose( false, pager_adapter.getColumn( pager.getCurrentItem() ) );
}else{
super.onBackPressed();
}
@ -211,7 +212,7 @@ public class ActMain extends AppCompatActivity
performAddTimeline( Column.TYPE_TL_NOTIFICATIONS );
}else if( id == R.id.nav_app_setting ){
performAppSetting( );
performAppSetting();
}else if( id == R.id.nav_account_setting ){
performAccountSetting();
}else if( id == R.id.nav_column_list ){
@ -283,7 +284,7 @@ public class ActMain extends AppCompatActivity
}
public void performAccountAdd(){
LoginForm.showLoginForm( this, null,new LoginForm.LoginFormCallback() {
LoginForm.showLoginForm( this, null, new LoginForm.LoginFormCallback() {
@Override
public void startLogin( final Dialog dialog, final String instance, final String user_mail, final String password ){
@ -318,8 +319,8 @@ public class ActMain extends AppCompatActivity
TootApiResult result = api_client.request( "/api/v1/accounts/verify_credentials" );
if( result != null && result.object != null ){
TootAccount ta = TootAccount.parse( log, result.object );
String user = ta.username +"@" + instance;
this.row_id = SavedAccount.insert( instance, user, result.object ,result.token_info );
String user = ta.username + "@" + instance;
this.row_id = SavedAccount.insert( instance, user, result.object, result.token_info );
}
return result;
}
@ -357,9 +358,8 @@ public class ActMain extends AppCompatActivity
}
public void performColumnClose( boolean bConfirm,final Column column ){
if(! bConfirm && ! pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN,false ) ){
public void performColumnClose( boolean bConfirm, final Column column ){
if( ! bConfirm && ! pref.getBoolean( Pref.KEY_DONT_CONFIRM_BEFORE_CLOSE_COLUMN, false ) ){
new AlertDialog.Builder( this )
.setTitle( R.string.confirm )
.setMessage( R.string.close_column )
@ -367,7 +367,7 @@ public class ActMain extends AppCompatActivity
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
performColumnClose( true,column );
performColumnClose( true, column );
}
} )
.show();
@ -379,58 +379,56 @@ public class ActMain extends AppCompatActivity
if( pager_adapter.getCount() == 0 ){
llEmpty.setVisibility( View.VISIBLE );
}else if( page_showing > 0 && page_showing == page_delete ){
pager.setCurrentItem( page_showing-1 ,true);
pager.setCurrentItem( page_showing - 1, true );
}
}
//////////////////////////////////////////////////////////////
// カラム追加系
public void addColumn(SavedAccount ai,int type,long who,long status_id ){
public void addColumn( SavedAccount ai, int type, long who, long status_id ){
// 既に同じカラムがあればそこに移動する
for( Column column : pager_adapter.column_list ){
if( ai.user.equals( column.access_info.user )
&& column.type == type
&& column.who_id == who
&& column.status_id == status_id
){
pager.setCurrentItem( pager_adapter.column_list.indexOf( column ) ,true);
){
pager.setCurrentItem( pager_adapter.column_list.indexOf( column ), true );
return;
}
}
llEmpty.setVisibility( View.GONE );
//
Column col = new Column( ActMain.this, ai, type, who,status_id );
Column col = new Column( ActMain.this, ai, type, who, status_id );
int idx = pager_adapter.addColumn( pager, col );
pager.setCurrentItem( idx ,true);
pager.setCurrentItem( idx, true );
}
private void onAccountUpdated( SavedAccount data ){
Utils.showToast( this, false, R.string.account_confirmed );
addColumn(data, Column.TYPE_TL_HOME,data.id ,0L );
addColumn( data, Column.TYPE_TL_HOME, data.id, 0L );
}
void performOpenUser(SavedAccount access_info,TootAccount user){
addColumn( access_info,Column.TYPE_TL_STATUSES, user.id ,0L);
void performOpenUser( SavedAccount access_info, TootAccount user ){
addColumn( access_info, Column.TYPE_TL_STATUSES, user.id, 0L );
}
public void performConversation( SavedAccount access_info, TootStatus status ){
addColumn( access_info,Column.TYPE_TL_CONVERSATION, access_info.id, status.id );
addColumn( access_info, Column.TYPE_TL_CONVERSATION, access_info.id, status.id );
}
private void performAddTimeline( final int type ){
AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
@Override
public void onAccountPicked( SavedAccount ai ){
addColumn( ai, type,ai.id ,0L);
addColumn( ai, type, ai.id, 0L );
}
} );
}
//////////////////////////////////////////////////////////////
public void openBrowser( String url ){
openChromeTab( url );
@ -448,7 +446,7 @@ public class ActMain extends AppCompatActivity
}catch( Throwable ex ){
// ex.printStackTrace();
log.e( ex, "openChromeTab failed. url=%s",url );
log.e( ex, "openChromeTab failed. url=%s", url );
}
}
@ -458,15 +456,20 @@ public class ActMain extends AppCompatActivity
openChromeTab( url );
}
};
private void performTootButton(){
Column c = pager_adapter.getColumn( pager.getCurrentItem() );
if( c != null && c.access_info != null ){
ActPost.open( this, c.access_info.db_id ,null );
ActPost.open( this, c.access_info.db_id, "" );
}
}
public void performReply( SavedAccount account, TootStatus status ){
ActPost.open( this, account.db_id ,status );
ActPost.open( this, account.db_id, status );
}
public void performMention( SavedAccount account, TootAccount who ){
ActPost.open( this, account.db_id, account.getFullAcct( who ) +" " );
}
/////////////////////////////////////////////////////////////////////////
@ -479,29 +482,28 @@ public class ActMain extends AppCompatActivity
}
}
/////////////////////////////////////////////////////////////////////////
// favourite
final HashSet<String> map_busy_fav = new HashSet<>( );
final HashSet< String > map_busy_fav = new HashSet<>();
boolean isBusyFav(SavedAccount account,TootStatus status){
String busy_key = account.host+":"+ status.id;
return map_busy_fav.contains(busy_key);
boolean isBusyFav( SavedAccount account, TootStatus status ){
String busy_key = account.host + ":" + status.id;
return map_busy_fav.contains( busy_key );
}
public void performFavourite( final SavedAccount account, final TootStatus status ){
//
final String busy_key = account.host+":"+ status.id;
final String busy_key = account.host + ":" + status.id;
//
if( map_busy_fav.contains(busy_key) ){
Utils.showToast( this,false,R.string.wait_previous_operation );
if( map_busy_fav.contains( busy_key ) ){
Utils.showToast( this, false, R.string.wait_previous_operation );
return;
}
//
map_busy_fav.add( busy_key );
//
new AsyncTask<Void,Void,TootApiResult>(){
new AsyncTask< Void, Void, TootApiResult >() {
final boolean new_state = ! status.favourited;
TootStatus new_status;
@ -522,19 +524,19 @@ public class ActMain extends AppCompatActivity
Request.Builder request_builder = new Request.Builder()
.post( RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
,""
));
, ""
) );
TootApiResult result = client.request(
(new_state
? "/api/v1/statuses/"+status.id+"/favourite"
: "/api/v1/statuses/"+status.id+"/unfavourite"
( new_state
? "/api/v1/statuses/" + status.id + "/favourite"
: "/api/v1/statuses/" + status.id + "/unfavourite"
)
, request_builder );
if( result.object != null ){
new_status = TootStatus.parse( log,result.object );
new_status = TootStatus.parse( log, result.object );
}
return result;
}
@ -546,15 +548,15 @@ public class ActMain extends AppCompatActivity
@Override
protected void onPostExecute( TootApiResult result ){
map_busy_fav.remove( busy_key);
if( new_status != null ){
map_busy_fav.remove( busy_key );
if( new_status != null ){
// カウント数は遅延があるみたい
if( new_state && new_status.favourites_count <= status.favourites_count ){
// 星つけたのにカウントが上がらないのは違和感あるので表示をいじる
new_status.favourites_count = status.favourites_count +1;
}else if( !new_state && new_status.favourites_count >= status.favourites_count ){
new_status.favourites_count = status.favourites_count + 1;
}else if( ! new_state && new_status.favourites_count >= status.favourites_count ){
// 星外したのにカウントが下がらないのは違和感あるので表示をいじる
new_status.favourites_count = status.favourites_count -1;
new_status.favourites_count = status.favourites_count - 1;
if( new_status.favourites_count < 0 ){
new_status.favourites_count = 0;
}
@ -566,34 +568,32 @@ public class ActMain extends AppCompatActivity
status.favourited = new_status.favourited;
status.favourites_count = new_status.favourites_count;
}
});
} );
}
}else{
if( result != null) Utils.showToast( ActMain.this,true,result.error );
if( result != null ) Utils.showToast( ActMain.this, true, result.error );
}
showColumnMatchAccount(account);
showColumnMatchAccount( account );
}
}.execute();
showColumnMatchAccount(account);
showColumnMatchAccount( account );
}
/////////////////////////////////////////////////////////////////////////
// boost
final HashSet<String> map_busy_boost = new HashSet<>( );
final HashSet< String > map_busy_boost = new HashSet<>();
boolean isBusyBoost(SavedAccount account,TootStatus status){
String busy_key = account.host+":"+ status.id;
return map_busy_boost.contains( busy_key);
boolean isBusyBoost( SavedAccount account, TootStatus status ){
String busy_key = account.host + ":" + status.id;
return map_busy_boost.contains( busy_key );
}
public void performBoost( final SavedAccount account, final TootStatus status ,boolean bConfirmed){
public void performBoost( final SavedAccount account, final TootStatus status, boolean bConfirmed ){
//
final String busy_key = account.host + ":" + status.id;
//
if(map_busy_boost.contains( busy_key ) ){
if( map_busy_boost.contains( busy_key ) ){
Utils.showToast( this, false, R.string.wait_previous_operation );
return;
}
@ -605,27 +605,27 @@ public class ActMain extends AppCompatActivity
return;
}
}else{
if(!bConfirmed && account.confirm_boost ){
if( ! bConfirmed && account.confirm_boost ){
// TODO: アカウント設定でスキップさせたい
new AlertDialog.Builder(this)
.setTitle(R.string.confirm)
.setMessage(R.string.confirm_boost)
new AlertDialog.Builder( this )
.setTitle( R.string.confirm )
.setMessage( R.string.confirm_boost )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
performBoost( account, status ,true);
performBoost( account, status, true );
}
} )
.setNegativeButton( R.string.cancel,null )
.setNegativeButton( R.string.cancel, null )
.show();
return;
}
}
//
map_busy_boost.add( busy_key);
map_busy_boost.add( busy_key );
//
new AsyncTask<Void,Void,TootApiResult>(){
new AsyncTask< Void, Void, TootApiResult >() {
final boolean new_state = ! status.reblogged;
TootStatus new_status;
@ -646,16 +646,16 @@ public class ActMain extends AppCompatActivity
Request.Builder request_builder = new Request.Builder()
.post( RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
,""
));
, ""
) );
TootApiResult result = client.request(
"/api/v1/statuses/"+status.id+(new_state ? "/reblog" : "/unreblog")
"/api/v1/statuses/" + status.id + ( new_state ? "/reblog" : "/unreblog" )
, request_builder );
if( result.object != null ){
// reblog,unreblog のレスポンスは信用ならんのでステータスを再取得する
result = client.request("/api/v1/statuses/"+status.id);
result = client.request( "/api/v1/statuses/" + status.id );
if( result.object != null ){
new_status = TootStatus.parse( log, result.object );
}
@ -672,15 +672,15 @@ public class ActMain extends AppCompatActivity
@Override
protected void onPostExecute( TootApiResult result ){
map_busy_boost.remove( busy_key);
if( new_status != null ){
map_busy_boost.remove( busy_key );
if( new_status != null ){
// カウント数は遅延があるみたい
if( new_status.reblogged && new_status.reblogs_count <= status.reblogs_count ){
// 星つけたのにカウントが上がらないのは違和感あるので表示をいじる
new_status.reblogs_count = status.reblogs_count +1;
}else if( !new_status.reblogged && new_status.reblogs_count >= status.reblogs_count ){
new_status.reblogs_count = status.reblogs_count + 1;
}else if( ! new_status.reblogged && new_status.reblogs_count >= status.reblogs_count ){
// 星外したのにカウントが下がらないのは違和感あるので表示をいじる
new_status.reblogs_count = status.reblogs_count -1;
new_status.reblogs_count = status.reblogs_count - 1;
if( new_status.reblogs_count < 0 ){
new_status.reblogs_count = 0;
}
@ -692,61 +692,44 @@ public class ActMain extends AppCompatActivity
status.reblogged = new_status.reblogged;
status.reblogs_count = new_status.reblogs_count;
}
});
} );
}
}else{
if( result != null) Utils.showToast( ActMain.this,true,result.error );
if( result != null ) Utils.showToast( ActMain.this, true, result.error );
}
showColumnMatchAccount(account);
showColumnMatchAccount( account );
}
}.execute();
showColumnMatchAccount(account);
showColumnMatchAccount( account );
}
////////////////////////////////////////
public void performMore( SavedAccount account, TootStatus status ){
// open menu
// Expand this status
// Mute user
// Block user
// report user
Utils.showToast( this,false,"not implemented. toot="+status.decoded_content );
}
////////////////////////////////////////
private void performAccountSetting(){
AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
@Override
public void onAccountPicked( SavedAccount ai ){
ActAccountSetting.open( ActMain.this, ai);
ActAccountSetting.open( ActMain.this, ai );
}
} );
}
private void performAppSetting(){
ActAppSetting.open( ActMain.this);
ActAppSetting.open( ActMain.this );
}
////////////////////////////////////////////////////////
// column list
JSONArray encodeColumnList(){
JSONArray array = new JSONArray();
for(int i=0,ie = pager_adapter.column_list.size(); i<ie;++i){
Column column = pager_adapter.column_list.get(i);
for( int i = 0, ie = pager_adapter.column_list.size() ; i < ie ; ++ i ){
Column column = pager_adapter.column_list.get( i );
try{
JSONObject dst = new JSONObject();
column.encodeJSON( dst ,i);
column.encodeJSON( dst, i );
array.put( dst );
}catch( JSONException ex ){
ex.printStackTrace();
@ -756,16 +739,14 @@ public class ActMain extends AppCompatActivity
}
private void performColumnList(){
Intent intent = new Intent(this,ActColumnList.class);
intent.putExtra(ActColumnList.EXTRA_ORDER,encodeColumnList().toString() );
intent.putExtra(ActColumnList.EXTRA_SELECTION,pager.getCurrentItem() );
startActivityForResult( intent ,REQUEST_CODE_COLUMN_LIST );
Intent intent = new Intent( this, ActColumnList.class );
intent.putExtra( ActColumnList.EXTRA_ORDER, encodeColumnList().toString() );
intent.putExtra( ActColumnList.EXTRA_SELECTION, pager.getCurrentItem() );
startActivityForResult( intent, REQUEST_CODE_COLUMN_LIST );
}
static final String FILE_COLUMN_LIST = "column_list";
private void saveColumnList(){
JSONArray array = encodeColumnList();
try{
@ -815,5 +796,341 @@ public class ActMain extends AppCompatActivity
llEmpty.setVisibility( View.GONE );
}
}
public void performFollow( final SavedAccount account, final TootAccount who ){
String[] caption_list = new String[]{
getString( R.string.follow ),
getString( R.string.unfollow ),
};
new AlertDialog.Builder( this )
.setNegativeButton( R.string.cancel, null )
.setItems( caption_list, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
switch( which ){
case 0:
performFollow( account, who, true );
break;
case 1:
performFollow( account, who, false );
break;
}
}
} )
.show();
}
private void performFollow( final SavedAccount account, final TootAccount who, final boolean bFollow ){
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( account );
TootApiResult result;
if( bFollow & who.acct.contains( "@" ) ){
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "uri=" + Uri.encode( who.acct )
) );
result = client.request( "/api/v1/follows", request_builder );
}else{
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "" // 空データ
) );
result = client.request( "/api/v1/accounts/" + who.id + ( bFollow ? "/follow" : "/unfollow" )
, request_builder );
}
if( result != null ){
if( result.object != null ){
Utils.showToast( ActMain.this, false, bFollow ? R.string.follow_succeeded : R.string.unfollow_succeeded );
}else if( bFollow && who.locked && result.response.code() == 422 ){
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
}else{
Utils.showToast( ActMain.this, false, result.error );
}
}
return result;
}
}.execute();
}
////////////////////////////////////////
private void performMute( final SavedAccount account, final TootAccount who, final boolean bMute ){
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( account );
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "" // 空データ
) );
TootApiResult result = client.request( "/api/v1/accounts/" + who.id + ( bMute ? "/mute" : "/unmute" )
, request_builder );
if( result != null ){
if( result.object != null ){
Utils.showToast( ActMain.this, false, bMute ? R.string.mute_succeeded : R.string.unmute_succeeded );
if( bMute ){
for( Column column : pager_adapter.column_list ){
column.removeStatusByAccount( account, who.id );
}
showColumnMatchAccount( account );
}
}else{
Utils.showToast( ActMain.this, false, result.error );
}
}
return result;
}
}.execute();
}
private void performBlock( final SavedAccount account, final TootAccount who, final boolean bBlock ){
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( account );
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "" // 空データ
) );
TootApiResult result = client.request( "/api/v1/accounts/" + who.id + ( bBlock ? "/block" : "/unblock" )
, request_builder );
if( result != null ){
if( result.object != null ){
Utils.showToast( ActMain.this, false, bBlock ? R.string.block_succeeded : R.string.unblock_succeeded );
if( bBlock ){
for( Column column : pager_adapter.column_list ){
column.removeStatusByAccount( account, who.id );
}
showColumnMatchAccount( account );
}
}else{
Utils.showToast( ActMain.this, false, result.error );
}
}
return result;
}
}.execute();
}
public interface ReportCompleteCallback {
void onReportComplete( TootApiResult result );
}
private void performReport( final SavedAccount account, final TootAccount who, final TootStatus status
, final String comment, final ReportCompleteCallback callback
){
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( account );
StringBuilder sb = new StringBuilder();
sb.append( "account_id=" )
.append( Long.toString( status.account.id ) )
.append( "&comment=" )
.append( Uri.encode( comment ) )
;
if( status != null ){
sb.append( "&status_ids[]=" )
.append( Long.toString( status.id ) )
;
}
Request.Builder request_builder = new Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, sb.toString()
) );
TootApiResult result = client.request( "/api/v1/reports", request_builder );
if( result != null ){
callback.onReportComplete( result );
}
return result;
}
}.execute();
}
private void performReport( final SavedAccount account, final TootAccount who, final TootStatus status ){
ReportForm.showReportForm( this, who, status, new ReportForm.ReportFormCallback() {
@Override
public void startReport( final Dialog dialog, String comment ){
performReport( account, who, status, comment, new ReportCompleteCallback() {
@Override
public void onReportComplete( TootApiResult result ){
if( result == null ){
// cancelled
return;
}else if( result.object != null ){
Utils.showToast( ActMain.this, false, R.string.report_completed );
dialog.dismiss();
}else{
// error
Utils.showToast( ActMain.this, false, result.error );
}
}
} );
}
} );
}
////////////////////////////////////////
// ステータスのmoreメニュー
public void performStatusMore( final SavedAccount account, final TootStatus status ){
String[] caption_list = new String[]{
getString( R.string.follow ),
getString( R.string.unfollow ),
getString( R.string.mute ),
getString( R.string.unmute ),
getString( R.string.block ),
getString( R.string.unblock ),
getString( R.string.report ),
};
new AlertDialog.Builder( this )
.setNegativeButton( R.string.cancel, null )
.setItems( caption_list, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
switch( which ){
case 0:
performFollow( account, status.account, true );
break;
case 1:
performFollow( account, status.account, false );
break;
case 2:
performMute( account, status.account, true );
break;
case 3:
performMute( account, status.account, false );
break;
case 4:
performBlock( account, status.account, true );
break;
case 5:
performBlock( account, status.account, false );
break;
case 6:
performReport( account, status.account, status );
break;
}
}
} )
.show();
}
public void performAccountMore( final SavedAccount account, final TootAccount who ){
String[] caption_list = new String[]{
getString( R.string.mention ),
getString( R.string.follow ),
getString( R.string.unfollow ),
getString( R.string.mute ),
getString( R.string.unmute ),
getString( R.string.block ),
getString( R.string.unblock ),
getString( R.string.report ),
};
new AlertDialog.Builder( this )
.setNegativeButton( R.string.cancel, null )
.setItems( caption_list, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
switch( which ){
case 0:
performMention( account, who );
break;
case 1:
performFollow( account, who, true );
break;
case 2:
performFollow( account, who, false );
break;
case 3:
performMute( account, who, true );
break;
case 4:
performMute( account, who, false );
break;
case 5:
performBlock( account, who, true );
break;
case 6:
performBlock( account, who, false );
break;
case 7:
performReport( account, who ,null );
break;
}
}
} )
.show();
}
}

View File

@ -57,6 +57,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
static final String KEY_ACCOUNT_DB_ID = "account_db_id";
static final String KEY_REPLY_STATUS = "reply_status";
static final String KEY_INITIAL_TEXT = "initial_text";
static final String KEY_ATTACHMENT_LIST = "attachment_list";
static final String KEY_VISIBILITY = "visibility";
@ -73,6 +74,14 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
context.startActivity( intent );
}
public static void open( Context context, long account_db_id, String initial_text ){
Intent intent = new Intent( context, ActPost.class );
intent.putExtra( KEY_ACCOUNT_DB_ID, account_db_id );
if( initial_text != null ){
intent.putExtra( KEY_INITIAL_TEXT, initial_text );
}
context.startActivity( intent );
}
@Override
public void onClick( View v ){
@ -197,7 +206,13 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener {
}
}
String sv = intent.getStringExtra( KEY_REPLY_STATUS );
String sv = intent.getStringExtra( KEY_INITIAL_TEXT );
if( sv != null){
etContent.setText(sv);
etContent.setSelection( sv.length() );
}
sv = intent.getStringExtra( KEY_REPLY_STATUS );
if( sv != null ){
try{
TootStatus repley_status = TootStatus.parse( log, new JSONObject( sv ) );

View File

@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -133,6 +134,7 @@ public class Column {
}
}
public interface StatusEntryCallback {
void onIterate( TootStatus status );
}
@ -154,6 +156,41 @@ public class Column {
}
}
}
// ミュートブロックが成功した時に呼ばれる
public void removeStatusByAccount( SavedAccount target_account, long who_id ){
if( target_account.user.equals( access_info.user ) ){
{
// remove from status_list
TootStatus.List tmp_list = new TootStatus.List( status_list.size() );
for( TootStatus status : status_list ){
if( status.account.id == who_id
|| ( status.reblog != null && status.reblog.account.id == who_id )
){
continue;
}
tmp_list.add( status );
}
status_list.clear();
status_list.addAll( tmp_list );
}
{
// remove from notification_list
TootNotification.List tmp_list = new TootNotification.List( notification_list.size() );
for( TootNotification item : notification_list ){
if( item.account.id == who_id ) continue;
if( item.status != null ){
if( item.status.account.id == who_id ) continue;
if( item.status.reblog != null && item.status.reblog.account.id == who_id ) continue;
}
tmp_list.add( item );
}
notification_list.clear();
notification_list.addAll( tmp_list );
}
}
}
public interface VisualCallback {
void onVisualColumn();
@ -180,11 +217,19 @@ public class Column {
}
}
public void fireVisualCallback(){
Iterator< VisualCallback > it = visual_callback.iterator();
while( it.hasNext() ){
it.next().onVisualColumn();
private final Runnable proc_fireVisualCallback = new Runnable() {
@Override
public void run(){
Iterator< VisualCallback > it = visual_callback.iterator();
while( it.hasNext() ){
it.next().onVisualColumn();
}
}
};
public void fireVisualCallback(){
Utils.runOnMainThread( proc_fireVisualCallback );
}
AsyncTask< Void, Void, TootApiResult > last_task;

View File

@ -307,8 +307,10 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
final Button btnFollowing;
final Button btnFollowers;
final Button btnStatusCount;
final View btnMore;
final TextView tvNote;
TootAccount who;
SavedAccount access_info;
public HeaderViewHolder( final ActMain activity, ListView parent ){
viewRoot = activity.getLayoutInflater().inflate( R.layout.lv_list_header, parent, false );
@ -321,17 +323,20 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
this.btnFollowers = (Button) viewRoot.findViewById( R.id.btnFollowers );
this.btnStatusCount = (Button) viewRoot.findViewById( R.id.btnStatusCount );
this.tvNote = (TextView) viewRoot.findViewById( R.id.tvNote );
this.btnMore = viewRoot.findViewById( R.id.btnMore );
ivBackground.setOnClickListener( this );
btnFollowing.setOnClickListener( this );
btnFollowers.setOnClickListener( this );
btnStatusCount.setOnClickListener( this );
btnMore.setOnClickListener( this );
tvNote.setMovementMethod( LinkMovementMethod.getInstance() );
}
public void bind( ActMain activity, SavedAccount access_info, TootAccount who ){
this.who = who;
this.access_info = access_info;
if( who == null ){
tvCreated.setText( "" );
ivBackground.setImageDrawable( null );
@ -378,6 +383,10 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
case R.id.btnStatusCount:
Utils.showToast( activity, false, "not implemented" );
break;
case R.id.btnMore:
activity.performAccountMore( access_info,who );
break;
}
}
}
@ -479,7 +488,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
ivMedia2.setOnClickListener( this );
ivMedia3.setOnClickListener( this );
ivMedia4.setOnClickListener( this );
btnFollow.setOnClickListener( this );
btnConversation.setOnClickListener( this );
btnReply.setOnClickListener( this );
btnBoost.setOnClickListener( this );
@ -726,7 +735,7 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
activity.performFavourite( account, status );
break;
case R.id.btnMore:
activity.performMore( account, status );
activity.performStatusMore( account, status );
break;
case R.id.ivThumbnail:
activity.performOpenUser( account, account_thumbnail );
@ -737,6 +746,8 @@ public class ColumnViewHolder implements View.OnClickListener, Column.VisualCall
case R.id.llFollow:
activity.performOpenUser( account, account_follow );
break;
case R.id.btnFollow:
activity.performAccountMore( account,account_follow);
}
}

View File

@ -8,7 +8,7 @@ import java.util.ArrayList;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class TootNotification extends TootId{
public class TootNotification extends TootId {
// The notification ID
//TootId public long id;
@ -42,10 +42,8 @@ public class TootNotification extends TootId{
dst.account = TootAccount.parse( log, src.optJSONObject( "account" ) );
dst.status = TootStatus.parse( log, src.optJSONObject( "status" ) );
dst.time_created_at = TootStatus.parseTime( log, dst.created_at );
return dst;
}catch( Throwable ex ){
ex.printStackTrace();
@ -54,9 +52,14 @@ public class TootNotification extends TootId{
}
}
public static class List extends ArrayList< TootNotification > {
public List(){
super();
}
public List( int capacity ){
super( capacity );
}
}
public static List parseList( LogCategory log, JSONArray array ){

View File

@ -17,11 +17,17 @@ import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class TootStatus extends TootId{
public class TootStatus extends TootId {
public static class List extends ArrayList< TootStatus > {
public List(){
super();
}
public List( int capacity ){
super( capacity );
}
}
// The ID of the status
@ -29,7 +35,7 @@ public class TootStatus extends TootId{
// A Fediverse-unique resource ID
public String uri;
//URL to the status page (can be remote)
public String url;
@ -71,10 +77,10 @@ public class TootStatus extends TootId{
//One of: public, unlisted, private, direct
public String visibility;
public static final String VISIBILITY_PUBLIC ="public";
public static final String VISIBILITY_UNLISTED ="unlisted";
public static final String VISIBILITY_PRIVATE ="private";
public static final String VISIBILITY_DIRECT ="direct";
public static final String VISIBILITY_PUBLIC = "public";
public static final String VISIBILITY_UNLISTED = "unlisted";
public static final String VISIBILITY_PRIVATE = "private";
public static final String VISIBILITY_DIRECT = "direct";
// An array of Attachments
public TootAttachment.List media_attachments;
@ -89,7 +95,7 @@ public class TootStatus extends TootId{
public String application;
public long time_created_at;
public Spannable decoded_content;
public Spannable decoded_tags;
public Spannable decoded_mentions;
@ -98,7 +104,6 @@ public class TootStatus extends TootId{
public boolean conversation_main;
public static TootStatus parse( LogCategory log, JSONObject src ){
if( src == null ) return null;
@ -106,14 +111,14 @@ public class TootStatus extends TootId{
try{
TootStatus status = new TootStatus();
status.json = src;
// log.d( "parse: %s", src.toString() );
// log.d( "parse: %s", src.toString() );
status.id = src.optLong( "id" );
status.uri = Utils.optStringX( src, "uri" );
status.url = Utils.optStringX( src, "url" );
status.account = TootAccount.parse( log, src.optJSONObject( "account" ) );
status.in_reply_to_id = Utils.optStringX( src, "in_reply_to_id" ); // null
status.in_reply_to_account_id = Utils.optStringX( src, "in_reply_to_account_id" ); // null
status.reblog = TootStatus.parse( log, src.optJSONObject( "reblog" ));
status.reblog = TootStatus.parse( log, src.optJSONObject( "reblog" ) );
status.content = Utils.optStringX( src, "content" );
status.created_at = Utils.optStringX( src, "created_at" ); // "2017-04-16T09:37:14.000Z"
status.reblogs_count = src.optLong( "reblogs_count" );
@ -124,15 +129,15 @@ public class TootStatus extends TootId{
status.spoiler_text = Utils.optStringX( src, "spoiler_text" ); // "",null, or CW text
status.visibility = Utils.optStringX( src, "visibility" );
status.media_attachments = TootAttachment.parseList( log, src.optJSONArray( "media_attachments" ) );
status.mentions = TootMention.parseList( log, src.optJSONArray( "mentions" ));
status.tags = TootTag.parseList( log, src.optJSONArray( "tags" ));
status.mentions = TootMention.parseList( log, src.optJSONArray( "mentions" ) );
status.tags = TootTag.parseList( log, src.optJSONArray( "tags" ) );
status.application = Utils.optStringX( src, "application" ); // null
status.time_created_at = parseTime( log, status.created_at );
status.decoded_content = HTMLDecoder.decodeHTML(status.content);
status.decoded_tags = HTMLDecoder.decodeTags( status.tags);
status.decoded_mentions = HTMLDecoder.decodeMentions( status.mentions);
status.decoded_content = HTMLDecoder.decodeHTML( status.content );
status.decoded_tags = HTMLDecoder.decodeTags( status.tags );
status.decoded_mentions = HTMLDecoder.decodeMentions( status.mentions );
return status;
}catch( Throwable ex ){
ex.printStackTrace();
@ -182,18 +187,18 @@ public class TootStatus extends TootId{
// aの方が大きい狭い)ならプラス
// IndexOutOfBoundsException 公開範囲が想定外
public static int compareVisibility( String a, String b ){
int ia = compareVisibility_tmp(a);
int ib = compareVisibility_tmp(b);
if( ia < ib ) return -1;
int ia = compareVisibility_tmp( a );
int ib = compareVisibility_tmp( b );
if( ia < ib ) return - 1;
if( ia > ib ) return 1;
return 0;
}
private static int compareVisibility_tmp( String a ){
if(TootStatus.VISIBILITY_DIRECT.equals( a ) ) return 0;
if(TootStatus.VISIBILITY_PRIVATE.equals( a ) ) return 1;
if(TootStatus.VISIBILITY_UNLISTED.equals( a ) ) return 2;
if(TootStatus.VISIBILITY_PUBLIC.equals( a ) ) return 3;
if( TootStatus.VISIBILITY_DIRECT.equals( a ) ) return 0;
if( TootStatus.VISIBILITY_PRIVATE.equals( a ) ) return 1;
if( TootStatus.VISIBILITY_UNLISTED.equals( a ) ) return 2;
if( TootStatus.VISIBILITY_PUBLIC.equals( a ) ) return 3;
throw new IndexOutOfBoundsException( "visibility not in range" );
}

View File

@ -0,0 +1,69 @@
package jp.juggler.subwaytooter.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.text.InputType;
import android.text.TextUtils;
import android.view.View;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.util.Utils;
/**
* Created by tateisu on 2017/04/23.
*/
public class ReportForm {
public interface ReportFormCallback{
void startReport( Dialog dialog, String comment);
}
public static void showReportForm( final Activity activity, TootAccount who, TootStatus status , final ReportFormCallback callback){
final View view = activity.getLayoutInflater().inflate( R.layout.dlg_report_user, null, false );
final TextView tvUser = (TextView) view.findViewById( R.id.tvUser );
final TextView tvStatus= (TextView) view.findViewById( R.id.tvStatus );
final EditText etComment= (EditText) view.findViewById( R.id.etComment );
tvUser.setText( who.acct );
tvStatus.setText( status == null ? "" : status.decoded_content);
final Dialog dialog = new Dialog( activity );
dialog.setContentView( view );
view.findViewById( R.id.btnOk ).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ){
final String comment = etComment.getText().toString().trim();
if( TextUtils.isEmpty( comment ) ){
Utils.showToast( activity, true, R.string.comment_empty );
return;
}
callback.startReport( dialog,comment );
}
} );
view.findViewById( R.id.btnCancel ).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ){
dialog.cancel();
}
} );
//noinspection ConstantConditions
dialog.getWindow().setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT );
dialog.show();
}
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/user"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:textSize="20sp"
android:id="@+id/tvUser"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/status"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:id="@+id/tvStatus"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/report_reason"
android:labelFor="@+id/etComment"
/>
<EditText
android:id="@+id/etComment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:inputType="text"
/>
<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>

View File

@ -108,6 +108,17 @@
tools:text="followers\n9999"
android:background="@drawable/btn_bg_transparent"
/>
<ImageButton
android:id="@+id/btnMore"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/more"
android:minWidth="48dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:src="@drawable/btn_more"
/>
</LinearLayout>

View File

@ -132,4 +132,21 @@
<string name="mail_address_not_contains_at_mark">please input user mail address that contains \'@\' and \'.\'</string>
<string name="instance_hint">ex) mastodon.social</string>
<string name="mail_hint">ex) your@e-mail.address</string>
<string name="unfollow">unfollow</string>
<string name="follow_succeeded">follow succeeded</string>
<string name="unfollow_succeeded">unfollow succeeded</string>
<string name="mute">mute</string>
<string name="unmute">unmute</string>
<string name="block">block</string>
<string name="unblock">unblock</string>
<string name="report">report</string>
<string name="cant_follow_locked_user">could not follow the non-public user</string>
<string name="unmute_succeeded">unmute succeeded</string>
<string name="mute_succeeded">mute succeeded</string>
<string name="unblock_succeeded">unblock succeeded</string>
<string name="block_succeeded">block succeeded</string>
<string name="report_reason">comment for report reason</string>
<string name="comment_empty">please comment for any reason to report</string>
<string name="report_completed">report completed.</string>
<string name="mention">mention</string>
</resources>