お気に入り操作時に変更中の表示が出なくなっていたバグの修正。トゥート検索機能を追加。
This commit is contained in:
parent
e5626c5539
commit
6f0454e76f
|
@ -2,6 +2,7 @@
|
|||
<dictionary name="tateisu">
|
||||
<words>
|
||||
<w>adamrocker</w>
|
||||
<w>apikey</w>
|
||||
<w>dont</w>
|
||||
<w>emoji</w>
|
||||
<w>emojione</w>
|
||||
|
@ -30,6 +31,7 @@
|
|||
<w>unfollow</w>
|
||||
<w>unmute</w>
|
||||
<w>unreblog</w>
|
||||
<w>utoken</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
|
@ -9,8 +9,8 @@ android {
|
|||
applicationId "jp.juggler.subwaytooter"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 93
|
||||
versionName "0.9.3"
|
||||
versionCode 95
|
||||
versionName "0.9.5"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ import jp.juggler.subwaytooter.api.entity.TootApplication;
|
|||
import jp.juggler.subwaytooter.api.entity.TootRelationShip;
|
||||
import jp.juggler.subwaytooter.api.entity.TootResults;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.dialog.AccountPicker;
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm;
|
||||
import jp.juggler.subwaytooter.dialog.LoginForm;
|
||||
|
@ -198,11 +199,16 @@ public class ActMain extends AppCompatActivity
|
|||
boolean bRemoved = false;
|
||||
for( int i = 0, ie = app_state.column_list.size() ; i < ie ; ++ i ){
|
||||
Column column = app_state.column_list.get( i );
|
||||
SavedAccount sa = SavedAccount.loadAccount( log, column.access_info.db_id );
|
||||
if( sa == null ){
|
||||
bRemoved = true;
|
||||
|
||||
if( column.access_info.isNA() ){
|
||||
// 検索カラムはアカウント削除とか無関係
|
||||
}else{
|
||||
new_order.add( i );
|
||||
SavedAccount sa = SavedAccount.loadAccount( log, column.access_info.db_id );
|
||||
if( sa == null ){
|
||||
bRemoved = true;
|
||||
}else{
|
||||
new_order.add( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( bRemoved ){
|
||||
|
@ -607,6 +613,9 @@ public class ActMain extends AppCompatActivity
|
|||
}else if( id == R.id.nav_muted_word ){
|
||||
startActivity( new Intent( this, ActMutedWord.class ) );
|
||||
|
||||
}else if( id == R.id.mastodon_search_portal ){
|
||||
addColumn( getDefaultInsertPosition(), SavedAccount.getNA(),Column.TYPE_SEARCH_PORTAL , "" );
|
||||
|
||||
// }else if( id == R.id.nav_translation ){
|
||||
// Intent intent = new Intent(this, TransCommuActivity.class);
|
||||
// intent.putExtra(TransCommuActivity.APPLICATION_CODE_EXTRA, "FJlDoBKitg");
|
||||
|
@ -1356,7 +1365,7 @@ public class ActMain extends AppCompatActivity
|
|||
SavedAccount a = column.access_info;
|
||||
if( done_list.contains( a ) ) continue;
|
||||
done_list.add( a );
|
||||
a.reloadSetting();
|
||||
if( !a.isNA() ) a.reloadSetting();
|
||||
column.fireShowColumnHeader();
|
||||
}
|
||||
}
|
||||
|
@ -1368,7 +1377,7 @@ public class ActMain extends AppCompatActivity
|
|||
if( ! Utils.equalsNullable( a.acct, account.acct ) ) continue;
|
||||
if( done_list.contains( a ) ) continue;
|
||||
done_list.add( a );
|
||||
a.reloadSetting();
|
||||
if( !a.isNA() ) a.reloadSetting();
|
||||
column.fireShowColumnHeader();
|
||||
}
|
||||
}
|
||||
|
@ -1463,6 +1472,7 @@ public class ActMain extends AppCompatActivity
|
|||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
public void performMuteApp( @NonNull TootApplication application ){
|
||||
MutedApp.save( application.name );
|
||||
|
@ -1557,6 +1567,47 @@ public class ActMain extends AppCompatActivity
|
|||
try{
|
||||
log.d( "openChromeTab url=%s", url );
|
||||
|
||||
if( !noIntercept && access_info != null && access_info.isNA() ){
|
||||
// トゥート検索カラムではaccess_infoは何にも紐ついていない
|
||||
|
||||
// ハッシュタグをアプリ内で開く
|
||||
Matcher m = reHashTag.matcher( url );
|
||||
if( m.find() ){
|
||||
// https://mastodon.juggler.jp/tags/%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%B0
|
||||
String host = m.group( 1 );
|
||||
String tag = Uri.decode( m.group( 2 ) );
|
||||
openHashTagOtherInstance( pos, access_info, url, host, tag );
|
||||
return;
|
||||
}
|
||||
|
||||
// ステータスページをアプリから開く
|
||||
m = reStatusPage.matcher( url );
|
||||
if( m.find() ){
|
||||
try{
|
||||
// https://mastodon.juggler.jp/@SubwayTooter/(status_id)
|
||||
final String host = m.group( 1 );
|
||||
final long status_id = Long.parseLong( m.group( 3 ), 10 );
|
||||
openStatusOtherInstance( pos, access_info, url, host, status_id );
|
||||
return;
|
||||
}catch( Throwable ex ){
|
||||
Utils.showToast( this, ex, "can't parse status id." );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ユーザページをアプリ内で開く
|
||||
m = reUserPage.matcher( url );
|
||||
if( m.find() ){
|
||||
// https://mastodon.juggler.jp/@SubwayTooter
|
||||
final String host = m.group( 1 );
|
||||
final String user = Uri.decode( m.group( 2 ) );
|
||||
|
||||
openProfileByHostUser( pos,access_info,url,host,user );
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( ! noIntercept && access_info != null ){
|
||||
// ハッシュタグをアプリ内で開く
|
||||
Matcher m = reHashTag.matcher( url );
|
||||
|
@ -1635,15 +1686,19 @@ public class ActMain extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
public void openStatus( int pos, @NonNull SavedAccount access_info, @NonNull TootStatus status ){
|
||||
openStatus( pos, access_info, status.id );
|
||||
public void openStatus( int pos, @NonNull SavedAccount access_info, @NonNull TootStatusLike status ){
|
||||
if( access_info.host.equalsIgnoreCase( status.status_host ) ){
|
||||
openStatus( pos, access_info, status.id );
|
||||
}else{
|
||||
openStatusOtherInstance( pos, access_info,status.url,status.status_host,status.id);
|
||||
}
|
||||
}
|
||||
|
||||
public void openStatus( int pos, @NonNull SavedAccount access_info, long status_id ){
|
||||
addColumn( pos, access_info, Column.TYPE_CONVERSATION, status_id );
|
||||
}
|
||||
|
||||
private void openStatusOtherInstance( final int pos, final SavedAccount access_info, final String url, final String host, final long status_id ){
|
||||
void openStatusOtherInstance( final int pos, final SavedAccount access_info, final String url, final String host, final long status_id ){
|
||||
ActionsDialog dialog = new ActionsDialog();
|
||||
|
||||
// ブラウザで表示する
|
||||
|
@ -1851,7 +1906,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
if( result != null && result.object != null ){
|
||||
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host, result.object );
|
||||
if( tmp != null ){
|
||||
if( tmp.accounts != null && ! tmp.accounts.isEmpty() ){
|
||||
who_local = tmp.accounts.get( 0 );
|
||||
|
@ -1974,7 +2029,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
public void performFavourite(
|
||||
final SavedAccount access_info
|
||||
, final TootStatus arg_status
|
||||
, final TootStatusLike arg_status
|
||||
, final int nCrossAccountMode
|
||||
, final boolean bSet
|
||||
, final RelationChangedCallback callback
|
||||
|
@ -2002,7 +2057,7 @@ public class ActMain extends AppCompatActivity
|
|||
client.setAccount( access_info );
|
||||
TootApiResult result;
|
||||
|
||||
TootStatus target_status;
|
||||
TootStatusLike target_status;
|
||||
if( nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE ){
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
String path = String.format( Locale.JAPAN, Column.PATH_SEARCH, Uri.encode( arg_status.url ) );
|
||||
|
@ -2013,7 +2068,7 @@ public class ActMain extends AppCompatActivity
|
|||
return result;
|
||||
}
|
||||
target_status = null;
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host,result.object );
|
||||
if( tmp != null ){
|
||||
if( tmp.statuses != null && ! tmp.statuses.isEmpty() ){
|
||||
target_status = tmp.statuses.get( 0 );
|
||||
|
@ -2043,7 +2098,7 @@ public class ActMain extends AppCompatActivity
|
|||
)
|
||||
, request_builder );
|
||||
if( result != null && result.object != null ){
|
||||
new_status = TootStatus.parse( log, access_info, result.object );
|
||||
new_status = TootStatus.parse( log, access_info, access_info.host, result.object );
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -2108,7 +2163,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
public void performBoost(
|
||||
final SavedAccount access_info
|
||||
, final TootStatus arg_status
|
||||
, final TootStatusLike arg_status
|
||||
, final int nCrossAccountMode
|
||||
, final boolean bSet
|
||||
, final boolean bConfirmed
|
||||
|
@ -2172,7 +2227,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
TootApiResult result;
|
||||
|
||||
TootStatus target_status;
|
||||
TootStatusLike target_status;
|
||||
if( nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE ){
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
String path = String.format( Locale.JAPAN, Column.PATH_SEARCH, Uri.encode( arg_status.url ) );
|
||||
|
@ -2183,7 +2238,7 @@ public class ActMain extends AppCompatActivity
|
|||
return result;
|
||||
}
|
||||
target_status = null;
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host,result.object );
|
||||
if( tmp != null ){
|
||||
if( tmp.statuses != null && ! tmp.statuses.isEmpty() ){
|
||||
target_status = tmp.statuses.get( 0 );
|
||||
|
@ -2213,7 +2268,7 @@ public class ActMain extends AppCompatActivity
|
|||
// reblog,unreblog のレスポンスは信用ならんのでステータスを再取得する
|
||||
result = client.request( "/api/v1/statuses/" + target_status.id );
|
||||
if( result != null && result.object != null ){
|
||||
new_status = TootStatus.parse( log, access_info, result.object );
|
||||
new_status = TootStatus.parse( log, access_info, access_info.host,result.object );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2276,14 +2331,18 @@ public class ActMain extends AppCompatActivity
|
|||
public void performReply(
|
||||
final SavedAccount access_info
|
||||
, final TootStatus arg_status
|
||||
, final boolean bRemote
|
||||
){
|
||||
if( ! bRemote ){
|
||||
ActPost.open( this, REQUEST_CODE_POST, access_info.db_id, arg_status );
|
||||
return;
|
||||
}
|
||||
ActPost.open( this, REQUEST_CODE_POST, access_info.db_id, arg_status );
|
||||
}
|
||||
|
||||
public void performReplyRemote(
|
||||
final SavedAccount access_info
|
||||
,final String remote_status_url
|
||||
,final long remote_status_id
|
||||
){
|
||||
final ProgressDialog progress = new ProgressDialog( this );
|
||||
|
||||
new AsyncTask< Void, Void, TootApiResult >() {
|
||||
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
||||
TootStatus target_status;
|
||||
|
||||
@Override protected TootApiResult doInBackground( Void... params ){
|
||||
|
@ -2298,15 +2357,15 @@ public class ActMain extends AppCompatActivity
|
|||
client.setAccount( access_info );
|
||||
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
String path = String.format( Locale.JAPAN, Column.PATH_SEARCH, Uri.encode( arg_status.url ) );
|
||||
String path = String.format( Locale.JAPAN, Column.PATH_SEARCH, Uri.encode( remote_status_url ) );
|
||||
path = path + "&resolve=1";
|
||||
|
||||
TootApiResult result = client.request( path );
|
||||
if( result != null && result.object != null ){
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host,result.object );
|
||||
if( tmp != null && tmp.statuses != null && ! tmp.statuses.isEmpty() ){
|
||||
target_status = tmp.statuses.get( 0 );
|
||||
log.d( "status id conversion %s => %s", arg_status.id, target_status.id );
|
||||
log.d( "status id conversion %s => %s", remote_status_id, target_status.id );
|
||||
}
|
||||
if( target_status == null ){
|
||||
return new TootApiResult( getString( R.string.status_id_conversion_failed ) );
|
||||
|
@ -2322,6 +2381,7 @@ public class ActMain extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
protected void onPostExecute( TootApiResult result ){
|
||||
progress.dismiss();
|
||||
if( result == null ){
|
||||
// cancelled.
|
||||
}else if( target_status != null ){
|
||||
|
@ -2330,7 +2390,18 @@ public class ActMain extends AppCompatActivity
|
|||
Utils.showToast( ActMain.this, true, result.error );
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor( App1.task_executor );
|
||||
};
|
||||
|
||||
progress.setIndeterminate( true );
|
||||
progress.setCancelable( true );
|
||||
progress.setMessage( getString(R.string.progress_synchronize_toot) );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
task.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
@ -3251,7 +3322,7 @@ public class ActMain extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
void openBoostFromAnotherAccount( @NonNull final SavedAccount timeline_account, final TootStatus status ){
|
||||
void openBoostFromAnotherAccount( @NonNull final SavedAccount timeline_account, @Nullable final TootStatusLike status ){
|
||||
if( status == null ) return;
|
||||
AccountPicker.pick( this, false, false
|
||||
, getString( R.string.account_picker_boost )
|
||||
|
@ -3270,7 +3341,7 @@ public class ActMain extends AppCompatActivity
|
|||
} );
|
||||
}
|
||||
|
||||
void openFavouriteFromAnotherAccount( @NonNull final SavedAccount timeline_account, final TootStatus status ){
|
||||
void openFavouriteFromAnotherAccount( @NonNull final SavedAccount timeline_account, final TootStatusLike status ){
|
||||
if( status == null ) return;
|
||||
AccountPicker.pick( this, false, false
|
||||
, getString( R.string.account_picker_favourite )
|
||||
|
@ -3288,25 +3359,38 @@ public class ActMain extends AppCompatActivity
|
|||
} );
|
||||
}
|
||||
|
||||
void openReplyFromAnotherAccount( @NonNull final SavedAccount access_info, final TootStatus status ){
|
||||
if( status == null ) return;
|
||||
|
||||
void openReplyFromAnotherAccount( final TootStatusLike status){
|
||||
AccountPicker.pick( this, false, false
|
||||
, getString( R.string.account_picker_reply )
|
||||
, makeAccountListNonPseudo( log ), new AccountPicker.AccountPickerCallback() {
|
||||
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
||||
performReply(
|
||||
ai
|
||||
, status
|
||||
, ! ai.host.equalsIgnoreCase( access_info.host )
|
||||
);
|
||||
if( (status instanceof TootStatus) && ai.host.equalsIgnoreCase( status.status_host ) ){
|
||||
performReply( ai, (TootStatus)status );
|
||||
}else{
|
||||
performReplyRemote( ai,status.url,status.id );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void openFollowFromAnotherAccount( @NonNull SavedAccount access_info, TootStatus status ){
|
||||
if( status == null ) return;
|
||||
openFollowFromAnotherAccount( access_info, status.account );
|
||||
}
|
||||
// void openReplyFromAnotherAccount( @NonNull final SavedAccount access_info, final String status_url,final long status_id ){
|
||||
//
|
||||
// final String status_host = getHostFromStatusUrl(status_url);
|
||||
// if( status_host ==null ) return;
|
||||
//
|
||||
// AccountPicker.pick( this, false, false
|
||||
// , getString( R.string.account_picker_reply )
|
||||
// , makeAccountListNonPseudo( log ), new AccountPicker.AccountPickerCallback() {
|
||||
// @Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
||||
// performReplyRemote( ai,status_url,status_id );
|
||||
// }
|
||||
// } );
|
||||
// }
|
||||
// void openFollowFromAnotherAccount( @NonNull SavedAccount access_info, TootStatus status ){
|
||||
// if( status == null ) return;
|
||||
// openFollowFromAnotherAccount( access_info, status.account );
|
||||
// }
|
||||
|
||||
void openFollowFromAnotherAccount( @NonNull SavedAccount access_info, final TootAccount account ){
|
||||
if( account == null ) return;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ClipData;
|
||||
|
@ -25,14 +26,12 @@ import android.support.v4.content.ContextCompat;
|
|||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
|
@ -47,7 +46,6 @@ import org.json.JSONArray;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -397,7 +395,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
sv = intent.getStringExtra( KEY_REPLY_STATUS );
|
||||
if( sv != null ){
|
||||
try{
|
||||
TootStatus reply_status = TootStatus.parse( log, account, new JSONObject( sv ) );
|
||||
TootStatus reply_status = TootStatus.parse( log, account, account.host,new JSONObject( sv ) );
|
||||
|
||||
// CW をリプライ元に合わせる
|
||||
if( ! TextUtils.isEmpty( reply_status.spoiler_text ) ){
|
||||
|
@ -898,7 +896,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
|
||||
TootApiResult result = client.request( path );
|
||||
if( result != null && result.object != null ){
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host,result.object );
|
||||
if( tmp != null && tmp.statuses != null && ! tmp.statuses.isEmpty() ){
|
||||
target_status = tmp.statuses.get( 0 );
|
||||
}
|
||||
|
@ -1607,7 +1605,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
|
||||
TootApiResult result = client.request( "/api/v1/statuses", request_builder );
|
||||
if( result != null && result.object != null ){
|
||||
status = TootStatus.parse( log, account, result.object );
|
||||
status = TootStatus.parse( log, account, account.host,result.object );
|
||||
|
||||
Spannable s = status.decoded_content;
|
||||
MyClickableSpan[] span_list = s.getSpans( 0, s.length(), MyClickableSpan.class );
|
||||
|
@ -1982,7 +1980,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( src.substring( 0, mushroom_start ) );
|
||||
int new_sel_start = sb.length();
|
||||
// int new_sel_start = sb.length();
|
||||
sb.append( text );
|
||||
int new_sel_end = sb.length();
|
||||
sb.append( src.substring( mushroom_end ) );
|
||||
|
@ -2028,6 +2026,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void showRecommendedPlugin( String title ){
|
||||
String language_code = getString( R.string.language_code );
|
||||
int res_id;
|
||||
|
@ -2038,46 +2038,37 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
|||
}else{
|
||||
res_id = R.raw.recommended_plugin_en;
|
||||
}
|
||||
try{
|
||||
InputStream is = getResources().openRawResource( res_id );
|
||||
try{
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
IOUtils.copy( is, bao );
|
||||
String text = Utils.decodeUTF8( bao.toByteArray() );
|
||||
|
||||
View viewRoot = getLayoutInflater().inflate( R.layout.dlg_plugin_missing, null, false );
|
||||
|
||||
TextView tvText = (TextView) viewRoot.findViewById( R.id.tvText );
|
||||
LinkClickContext lcc = new LinkClickContext() {
|
||||
@Override public AcctColor findAcctColor( String url ){
|
||||
return null;
|
||||
}
|
||||
};
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( lcc, text, false, null );
|
||||
tvText.setText( sv );
|
||||
tvText.setMovementMethod( LinkMovementMethod.getInstance() );
|
||||
|
||||
TextView tvTitle = (TextView) viewRoot.findViewById( R.id.tvTitle );
|
||||
if( TextUtils.isEmpty( title ) ){
|
||||
tvTitle.setVisibility( View.GONE );
|
||||
}else{
|
||||
tvTitle.setText( title );
|
||||
|
||||
byte[] data = Utils.loadRawResource(this,res_id);
|
||||
if( data != null ){
|
||||
String text = Utils.decodeUTF8( data );
|
||||
@SuppressLint("InflateParams")
|
||||
View viewRoot = getLayoutInflater().inflate( R.layout.dlg_plugin_missing, null, false );
|
||||
|
||||
TextView tvText = (TextView) viewRoot.findViewById( R.id.tvText );
|
||||
LinkClickContext lcc = new LinkClickContext() {
|
||||
@Override public AcctColor findAcctColor( String url ){
|
||||
return null;
|
||||
}
|
||||
};
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( lcc, text, false, null );
|
||||
tvText.setText( sv );
|
||||
tvText.setMovementMethod( LinkMovementMethod.getInstance() );
|
||||
|
||||
TextView tvTitle = (TextView) viewRoot.findViewById( R.id.tvTitle );
|
||||
if( TextUtils.isEmpty( title ) ){
|
||||
tvTitle.setVisibility( View.GONE );
|
||||
}else{
|
||||
tvTitle.setText( title );
|
||||
|
||||
new AlertDialog.Builder( this )
|
||||
.setView( viewRoot )
|
||||
.setCancelable( true )
|
||||
.setNeutralButton( R.string.close, null )
|
||||
.show();
|
||||
|
||||
}finally{
|
||||
IOUtils.closeQuietly( is );
|
||||
}
|
||||
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
new AlertDialog.Builder( this )
|
||||
.setView( viewRoot )
|
||||
.setCancelable( true )
|
||||
.setNeutralButton( R.string.close, null )
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final MyClickableSpan.LinkClickCallback link_click_listener = new MyClickableSpan.LinkClickCallback() {
|
||||
|
|
|
@ -13,6 +13,8 @@ import android.view.View;
|
|||
import android.widget.EditText;
|
||||
|
||||
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.table.MutedWord;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
|
@ -25,7 +27,7 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
|||
static final String EXTRA_TEXT = "text";
|
||||
static final String EXTRA_CONTENT_START = "content_start";
|
||||
|
||||
static void encodeStatus( Intent intent, Context context, SavedAccount access_info, TootStatus status ){
|
||||
static void encodeStatus( Intent intent, Context context, SavedAccount access_info, TootStatusLike status ){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( context.getString( R.string.send_header_url ) );
|
||||
sb.append( ": " );
|
||||
|
@ -33,7 +35,15 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
|||
sb.append( "\n" );
|
||||
sb.append( context.getString( R.string.send_header_date ) );
|
||||
sb.append( ": " );
|
||||
sb.append( TootStatus.formatTime( status.time_created_at ) );
|
||||
|
||||
if( status instanceof TootStatus ){
|
||||
TootStatus ts = (TootStatus)status;
|
||||
sb.append( TootStatus.formatTime( ts.time_created_at ) );
|
||||
}else if( status instanceof MSPToot ){
|
||||
MSPToot ts = (MSPToot)status;
|
||||
sb.append( ts.created_at );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
sb.append( context.getString( R.string.send_header_from_acct ) );
|
||||
sb.append( ": " );
|
||||
|
@ -63,7 +73,7 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
|||
}
|
||||
|
||||
|
||||
public static void open( ActMain activity, SavedAccount access_info, TootStatus status ){
|
||||
public static void open( ActMain activity, SavedAccount access_info, TootStatusLike status ){
|
||||
Intent intent = new Intent( activity, ActText.class );
|
||||
encodeStatus( intent,activity, access_info, status );
|
||||
|
||||
|
|
|
@ -636,7 +636,7 @@ public class AlarmService extends IntentService {
|
|||
return;
|
||||
}
|
||||
|
||||
TootNotification notification = TootNotification.parse( log, account, src );
|
||||
TootNotification notification = TootNotification.parse( log, account,account.host ,src );
|
||||
if( notification == null ){
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -328,6 +328,7 @@ public class AppDataExporter {
|
|||
case Pref.KEY_STREAM_LISTENER_SECRET:
|
||||
case Pref.KEY_STREAM_LISTENER_CONFIG_DATA:
|
||||
case Pref.KEY_CLIENT_NAME:
|
||||
case Pref.KEY_MASTODON_SEARCH_PORTAL_USER_TOKEN:
|
||||
String sv = reader.nextString();
|
||||
e.putString( k, sv );
|
||||
break;
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.LinkedList;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.MyClickableSpan;
|
||||
|
@ -134,34 +135,34 @@ class AppState {
|
|||
|
||||
private 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, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_fav.contains( key );
|
||||
}
|
||||
boolean setBusyFav( SavedAccount account, TootStatus status ){
|
||||
final String busy_key = account.acct +":" + status.uri;
|
||||
return map_busy_fav.add( busy_key );
|
||||
boolean setBusyFav( SavedAccount account, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_fav.add( key );
|
||||
}
|
||||
boolean resetBusyFav( SavedAccount account, TootStatus status ){
|
||||
final String busy_key = account.acct +":" + status.uri;
|
||||
return map_busy_fav.remove( busy_key );
|
||||
boolean resetBusyFav( SavedAccount account, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_fav.remove( key );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
private final HashSet< String > map_busy_boost = new HashSet<>();
|
||||
|
||||
boolean isBusyBoost( @NonNull SavedAccount account, @NonNull TootStatus status ){
|
||||
final String busy_key = account.acct +":" + status.uri;
|
||||
return map_busy_boost.contains( busy_key );
|
||||
boolean isBusyBoost( @NonNull SavedAccount account, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_boost.contains( key );
|
||||
}
|
||||
boolean setBusyBoost( SavedAccount account, TootStatus status ){
|
||||
final String busy_key = account.acct +":" + status.uri;
|
||||
return map_busy_boost.add( busy_key );
|
||||
boolean setBusyBoost( SavedAccount account, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_boost.add( key );
|
||||
}
|
||||
boolean resetBusyBoost( SavedAccount account, TootStatus status ){
|
||||
final String busy_key = account.acct +":" + status.uri;
|
||||
return map_busy_boost.remove( busy_key );
|
||||
boolean resetBusyBoost( SavedAccount account, @NonNull TootStatusLike status ){
|
||||
final String key = account.acct +":" + status.status_host + ":" + status.id;
|
||||
return map_busy_boost.remove( key );
|
||||
}
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ import jp.juggler.subwaytooter.api.entity.TootReport;
|
|||
import jp.juggler.subwaytooter.api.entity.TootResults;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootTag;
|
||||
import jp.juggler.subwaytooter.api_msp.MSPApiResult;
|
||||
import jp.juggler.subwaytooter.api_msp.MSPClient;
|
||||
import jp.juggler.subwaytooter.api_msp.entity.MSPToot;
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.table.AcctSet;
|
||||
import jp.juggler.subwaytooter.table.MutedApp;
|
||||
|
@ -162,6 +165,7 @@ class Column implements StreamReader.Callback {
|
|||
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_SEARCH_PORTAL = 17;
|
||||
|
||||
@NonNull final Context context;
|
||||
@NonNull private final AppState app_state;
|
||||
|
@ -229,7 +233,10 @@ class Column implements StreamReader.Callback {
|
|||
this.search_query = (String) getParamAt( params, 0 );
|
||||
this.search_resolve = (Boolean) getParamAt( params, 1 );
|
||||
break;
|
||||
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
this.search_query = (String) getParamAt( params, 0 );
|
||||
break;
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
@ -271,6 +278,9 @@ class Column implements StreamReader.Callback {
|
|||
item.put( KEY_SEARCH_QUERY, search_query );
|
||||
item.put( KEY_SEARCH_RESOLVE, search_resolve );
|
||||
break;
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
item.put( KEY_SEARCH_QUERY, search_query );
|
||||
break;
|
||||
}
|
||||
|
||||
// 以下は保存には必要ないが、カラムリスト画面で使う
|
||||
|
@ -286,9 +296,15 @@ class Column implements StreamReader.Callback {
|
|||
this.app_state = app_state;
|
||||
this.context = app_state.context;
|
||||
|
||||
SavedAccount ac = SavedAccount.loadAccount( log, src.optLong( KEY_ACCOUNT_ROW_ID ) );
|
||||
if( ac == null ) throw new RuntimeException( "missing account" );
|
||||
this.access_info = ac;
|
||||
long account_db_id = src.optLong( KEY_ACCOUNT_ROW_ID );
|
||||
if( account_db_id >= 0 ){
|
||||
SavedAccount ac = SavedAccount.loadAccount( log, account_db_id );
|
||||
if( ac == null ) throw new RuntimeException( "missing account" );
|
||||
this.access_info = ac;
|
||||
}else{
|
||||
this.access_info = SavedAccount.getNA();
|
||||
}
|
||||
|
||||
this.column_type = src.optInt( KEY_TYPE );
|
||||
this.dont_close = src.optBoolean( KEY_DONT_CLOSE );
|
||||
this.with_attachment = src.optBoolean( KEY_WITH_ATTACHMENT );
|
||||
|
@ -328,7 +344,9 @@ class Column implements StreamReader.Callback {
|
|||
this.search_query = src.optString( KEY_SEARCH_QUERY );
|
||||
this.search_resolve = src.optBoolean( KEY_SEARCH_RESOLVE, false );
|
||||
break;
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
this.search_query = src.optString( KEY_SEARCH_QUERY );
|
||||
break;
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
@ -375,7 +393,13 @@ class Column implements StreamReader.Callback {
|
|||
}catch( Throwable ex ){
|
||||
return false;
|
||||
}
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
try{
|
||||
String q = (String) getParamAt( params, 0 );
|
||||
return Utils.equalsNullable( q, this.search_query );
|
||||
}catch( Throwable ex ){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +441,13 @@ class Column implements StreamReader.Callback {
|
|||
return getColumnTypeName( context, column_type );
|
||||
}
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
if( bLong ){
|
||||
return context.getString( R.string.toot_search_of, search_query );
|
||||
}else{
|
||||
return getColumnTypeName( context, column_type );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,6 +502,9 @@ class Column implements StreamReader.Callback {
|
|||
case TYPE_SEARCH:
|
||||
return context.getString( R.string.search );
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
return context.getString( R.string.toot_search );
|
||||
|
||||
case TYPE_FOLLOW_REQUESTS:
|
||||
return context.getString( R.string.follow_requests );
|
||||
}
|
||||
|
@ -525,6 +559,9 @@ class Column implements StreamReader.Callback {
|
|||
case TYPE_SEARCH:
|
||||
return R.attr.ic_search;
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
return R.attr.ic_search;
|
||||
|
||||
case TYPE_FOLLOW_REQUESTS:
|
||||
return R.attr.ic_account_add;
|
||||
}
|
||||
|
@ -580,7 +617,7 @@ class Column implements StreamReader.Callback {
|
|||
for( Object o : list_data ){
|
||||
if( o instanceof TootStatus ){
|
||||
TootStatus item = (TootStatus) o;
|
||||
if( (item.account != null && item.account.id == who_id)
|
||||
if( ( item.account != null && item.account.id == who_id )
|
||||
|| ( item.reblog != null && item.reblog.account != null && item.reblog.account.id == who_id )
|
||||
){
|
||||
continue;
|
||||
|
@ -590,7 +627,8 @@ class Column implements StreamReader.Callback {
|
|||
TootNotification item = (TootNotification) o;
|
||||
if( item.account.id == who_id ) continue;
|
||||
if( item.status != null ){
|
||||
if( (item.status.account != null && item.status.account.id == who_id) ) continue;
|
||||
if( ( item.status.account != null && item.status.account.id == who_id ) )
|
||||
continue;
|
||||
if( item.status.reblog != null && item.status.reblog.account != null && item.status.reblog.account.id == who_id )
|
||||
continue;
|
||||
}
|
||||
|
@ -739,8 +777,9 @@ class Column implements StreamReader.Callback {
|
|||
for( Object o : list_data ){
|
||||
if( o instanceof TootStatus ){
|
||||
TootStatus item = (TootStatus) o;
|
||||
if( item.account != null && reDomain.matcher( item.account.acct ).find() ) continue;
|
||||
if( item.reblog != null && item.reblog.account !=null && reDomain.matcher( item.reblog.account.acct ).find() )
|
||||
if( item.account != null && reDomain.matcher( item.account.acct ).find() )
|
||||
continue;
|
||||
if( item.reblog != null && item.reblog.account != null && reDomain.matcher( item.reblog.account.acct ).find() )
|
||||
continue;
|
||||
}else if( o instanceof TootNotification ){
|
||||
TootNotification item = (TootNotification) o;
|
||||
|
@ -748,8 +787,9 @@ class Column implements StreamReader.Callback {
|
|||
if( reDomain.matcher( item.account.acct ).find() ) continue;
|
||||
}
|
||||
if( item.status != null ){
|
||||
if( item.status.account != null && reDomain.matcher( item.status.account.acct ).find() ) continue;
|
||||
if( item.status.reblog != null && item.status.reblog.account !=null && reDomain.matcher( item.status.reblog.account.acct ).find() )
|
||||
if( item.status.account != null && reDomain.matcher( item.status.account.acct ).find() )
|
||||
continue;
|
||||
if( item.status.reblog != null && item.status.reblog.account != null && reDomain.matcher( item.status.reblog.account.acct ).find() )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1022,7 +1062,7 @@ class Column implements StreamReader.Callback {
|
|||
if( result != null && result.array != null ){
|
||||
saveRange( result, true, true );
|
||||
//
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info, result.array );
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info, access_info.host,result.array );
|
||||
list_tmp = new ArrayList<>( src.size() );
|
||||
addWithFilter( list_tmp, src );
|
||||
//
|
||||
|
@ -1059,7 +1099,7 @@ class Column implements StreamReader.Callback {
|
|||
break;
|
||||
}
|
||||
|
||||
src = TootStatus.parseList( log, access_info, result2.array );
|
||||
src = TootStatus.parseList( log, access_info, access_info.host,result2.array );
|
||||
|
||||
addWithFilter( list_tmp, src );
|
||||
|
||||
|
@ -1106,7 +1146,7 @@ class Column implements StreamReader.Callback {
|
|||
TootApiResult result = client.request( path_base );
|
||||
if( result != null ){
|
||||
saveRange( result, true, true );
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info, result.array );
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info, access_info.host, result.array );
|
||||
|
||||
list_tmp = new ArrayList<>();
|
||||
addWithFilter( list_tmp, src );
|
||||
|
@ -1223,7 +1263,7 @@ class Column implements StreamReader.Callback {
|
|||
result = client.request(
|
||||
String.format( Locale.JAPAN, PATH_STATUSES, status_id ) );
|
||||
if( result == null || result.object == null ) return result;
|
||||
TootStatus target_status = TootStatus.parse( log, access_info, result.object );
|
||||
TootStatus target_status = TootStatus.parse( log, access_info, access_info.host,result.object );
|
||||
target_status.conversation_main = true;
|
||||
|
||||
// 前後の会話
|
||||
|
@ -1232,13 +1272,13 @@ class Column implements StreamReader.Callback {
|
|||
if( result == null || result.object == null ) return result;
|
||||
|
||||
// 一つのリストにまとめる
|
||||
TootContext context = TootContext.parse( log, access_info, result.object );
|
||||
list_tmp = new ArrayList<>( 1 + context.ancestors.size() + context.descendants.size() );
|
||||
if( context.ancestors != null )
|
||||
addWithFilter( list_tmp, context.ancestors );
|
||||
TootContext conversation_context = TootContext.parse( log, access_info, access_info.host,result.object );
|
||||
list_tmp = new ArrayList<>( 1 + conversation_context.ancestors.size() + conversation_context.descendants.size() );
|
||||
if( conversation_context.ancestors != null )
|
||||
addWithFilter( list_tmp, conversation_context.ancestors );
|
||||
list_tmp.add( target_status );
|
||||
if( context.descendants != null )
|
||||
addWithFilter( list_tmp, context.descendants );
|
||||
if( conversation_context.descendants != null )
|
||||
addWithFilter( list_tmp, conversation_context.descendants );
|
||||
|
||||
//
|
||||
return result;
|
||||
|
@ -1250,7 +1290,7 @@ class Column implements StreamReader.Callback {
|
|||
result = client.request( path );
|
||||
if( result == null || result.object == null ) return result;
|
||||
|
||||
TootResults tmp = TootResults.parse( log, access_info, result.object );
|
||||
TootResults tmp = TootResults.parse( log, access_info, access_info.host,result.object );
|
||||
if( tmp != null ){
|
||||
list_tmp = new ArrayList<>();
|
||||
list_tmp.addAll( tmp.hashtags );
|
||||
|
@ -1258,7 +1298,45 @@ class Column implements StreamReader.Callback {
|
|||
list_tmp.addAll( tmp.statuses );
|
||||
}
|
||||
return result;
|
||||
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
|
||||
max_id = "";
|
||||
String q = search_query.trim();
|
||||
if( q.length() <= 0 ){
|
||||
list_tmp = new ArrayList<>();
|
||||
result = new TootApiResult( context.getString( R.string.list_empty ) );
|
||||
}else{
|
||||
result = MSPClient.search( context, search_query, max_id, new MSPClient.Callback() {
|
||||
@Override
|
||||
public boolean isApiCancelled(){
|
||||
return isCancelled() || is_dispose.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishApiProgress( final String s ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override
|
||||
public void run(){
|
||||
if( isCancelled() ) return;
|
||||
task_progress = s;
|
||||
fireShowContent();
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
if( result != null && result.array != null ){
|
||||
// max_id の更新
|
||||
max_id = MSPClient.getMaxId( result.array, max_id );
|
||||
// リストデータの用意
|
||||
MSPToot.List search_result = MSPToot.parseList( log, access_info, result.array );
|
||||
if( search_result != null ){
|
||||
list_tmp = new ArrayList<>();
|
||||
list_tmp.addAll( search_result );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}finally{
|
||||
try{
|
||||
|
@ -1749,7 +1827,7 @@ class Column implements StreamReader.Callback {
|
|||
if( result != null && result.array != null ){
|
||||
saveRange( result, bBottom, ! bBottom );
|
||||
list_tmp = new ArrayList<>();
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info, result.array );
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info,access_info.host, result.array );
|
||||
addWithFilter( list_tmp, src );
|
||||
|
||||
if( ! src.isEmpty() ){
|
||||
|
@ -1793,7 +1871,7 @@ class Column implements StreamReader.Callback {
|
|||
break;
|
||||
}
|
||||
|
||||
src = TootNotification.parseList( log, access_info, result2.array );
|
||||
src = TootNotification.parseList( log, access_info, access_info.host,result2.array );
|
||||
if( ! src.isEmpty() ){
|
||||
addWithFilter( list_tmp, src );
|
||||
AlarmService.injectData( context, access_info.db_id, src );
|
||||
|
@ -1815,7 +1893,7 @@ class Column implements StreamReader.Callback {
|
|||
TootApiResult result = client.request( addRange( bBottom, path_base ) );
|
||||
if( result != null && result.array != null ){
|
||||
saveRange( result, bBottom, ! bBottom );
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info, result.array );
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info, access_info.host,result.array );
|
||||
list_tmp = new ArrayList<>();
|
||||
|
||||
addWithFilter( list_tmp, src );
|
||||
|
@ -1859,7 +1937,7 @@ class Column implements StreamReader.Callback {
|
|||
break;
|
||||
}
|
||||
|
||||
src = TootStatus.parseList( log, access_info, result2.array );
|
||||
src = TootStatus.parseList( log, access_info, access_info.host,result2.array );
|
||||
|
||||
addWithFilter( list_tmp, src );
|
||||
|
||||
|
@ -1915,7 +1993,7 @@ class Column implements StreamReader.Callback {
|
|||
break;
|
||||
}
|
||||
|
||||
src = TootStatus.parseList( log, access_info, result2.array );
|
||||
src = TootStatus.parseList( log, access_info, access_info.host,result2.array );
|
||||
addWithFilter( list_tmp, src );
|
||||
}
|
||||
}
|
||||
|
@ -2016,6 +2094,49 @@ class Column implements StreamReader.Callback {
|
|||
case TYPE_HASHTAG:
|
||||
return getStatusList( client,
|
||||
String.format( Locale.JAPAN, PATH_HASHTAG, Uri.encode( hashtag ) ) );
|
||||
|
||||
case TYPE_SEARCH_PORTAL:
|
||||
|
||||
if(!bBottom){
|
||||
return new TootApiResult( "head of list.");
|
||||
}
|
||||
|
||||
TootApiResult result;
|
||||
String q = search_query.trim();
|
||||
if( q.length() <= 0 ){
|
||||
list_tmp = new ArrayList<>();
|
||||
result = new TootApiResult( context.getString( R.string.list_empty ) );
|
||||
}else{
|
||||
result = MSPClient.search( context, search_query, max_id, new MSPClient.Callback() {
|
||||
@Override
|
||||
public boolean isApiCancelled(){
|
||||
return isCancelled() || is_dispose.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishApiProgress( final String s ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override
|
||||
public void run(){
|
||||
if( isCancelled() ) return;
|
||||
task_progress = s;
|
||||
fireShowContent();
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
if( result != null && result.array != null ){
|
||||
// max_id の更新
|
||||
max_id = MSPClient.getMaxId( result.array, max_id );
|
||||
// リストデータの用意
|
||||
MSPToot.List search_result = MSPToot.parseList( log, access_info, result.array );
|
||||
if( search_result != null ){
|
||||
list_tmp = new ArrayList<>();
|
||||
list_tmp.addAll( search_result );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}finally{
|
||||
try{
|
||||
|
@ -2271,7 +2392,7 @@ class Column implements StreamReader.Callback {
|
|||
}
|
||||
|
||||
result = r2;
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info, r2.array );
|
||||
TootNotification.List src = TootNotification.parseList( log, access_info, access_info.host,r2.array );
|
||||
|
||||
if( src.isEmpty() ){
|
||||
log.d( "gap-notification: empty." );
|
||||
|
@ -2327,7 +2448,7 @@ class Column implements StreamReader.Callback {
|
|||
// 成功した場合はそれを返したい
|
||||
result = r2;
|
||||
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info, r2.array );
|
||||
TootStatus.List src = TootStatus.parseList( log, access_info,access_info.host, r2.array );
|
||||
if( src.size() == 0 ){
|
||||
// 直前の取得でカラのデータが帰ってきたら終了
|
||||
log.d( "gap-statuses: empty." );
|
||||
|
@ -2698,7 +2819,8 @@ class Column implements StreamReader.Callback {
|
|||
}else if( o instanceof TootStatus ){
|
||||
TootStatus status = (TootStatus) o;
|
||||
if( column_type == TYPE_NOTIFICATIONS ) return;
|
||||
if( column_type == TYPE_LOCAL && status.account != null && status.account.acct.indexOf( '@' ) != - 1 ) return;
|
||||
if( column_type == TYPE_LOCAL && status.account != null && status.account.acct.indexOf( '@' ) != - 1 )
|
||||
return;
|
||||
if( isFiltered( status ) ) return;
|
||||
|
||||
if( this.enable_speech ){
|
||||
|
@ -2788,6 +2910,9 @@ class Column implements StreamReader.Callback {
|
|||
// リフレッシュしてからストリーミング開始
|
||||
log.d( "onResume: start auto refresh." );
|
||||
startRefresh( true, false, - 1L, - 1 );
|
||||
}else if( column_type == TYPE_SEARCH || column_type == TYPE_SEARCH_PORTAL ){
|
||||
// 検索カラムはリフレッシュもストリーミングもないが、resumeのタイミングでリストの再描画を行いたい
|
||||
fireShowContent();
|
||||
}else{
|
||||
// ギャップつきでストリーミング開始
|
||||
log.d( "onResume: start streaming with gap." );
|
||||
|
|
|
@ -9,6 +9,8 @@ import android.support.v4.view.ViewCompat;
|
|||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
@ -27,7 +29,9 @@ import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirec
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
|
||||
import jp.juggler.subwaytooter.view.MyListView;
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
@ -81,6 +85,8 @@ class ColumnViewHolder
|
|||
private final View llRegexFilter;
|
||||
private final Button btnDeleteNotification;
|
||||
|
||||
private final TextView tvSearchDesc;
|
||||
|
||||
ColumnViewHolder( ActMain arg_activity, View root ){
|
||||
this.activity = arg_activity;
|
||||
|
||||
|
@ -137,8 +143,8 @@ class ColumnViewHolder
|
|||
etRegexFilter = (EditText) root.findViewById( R.id.etRegexFilter );
|
||||
llRegexFilter = root.findViewById( R.id.llRegexFilter );
|
||||
tvRegexFilterError = (TextView) root.findViewById( R.id.tvRegexFilterError );
|
||||
|
||||
|
||||
tvSearchDesc = (TextView) root.findViewById( R.id.tvSearchDesc );
|
||||
|
||||
btnDeleteNotification = (Button) root.findViewById( R.id.btnDeleteNotification );
|
||||
|
||||
|
@ -223,7 +229,7 @@ class ColumnViewHolder
|
|||
|
||||
private boolean loading_busy;
|
||||
|
||||
void onPageCreate( Column column, int page_idx, int page_count ){
|
||||
void onPageCreate( @NonNull Column column, int page_idx, int page_count ){
|
||||
loading_busy = true;
|
||||
try{
|
||||
this.column = column;
|
||||
|
@ -250,6 +256,7 @@ class ColumnViewHolder
|
|||
bAllowFilter = true;
|
||||
break;
|
||||
case Column.TYPE_SEARCH:
|
||||
case Column.TYPE_SEARCH_PORTAL:
|
||||
case Column.TYPE_CONVERSATION:
|
||||
case Column.TYPE_REPORTS:
|
||||
case Column.TYPE_BLOCKS:
|
||||
|
@ -299,7 +306,8 @@ class ColumnViewHolder
|
|||
vg( llRegexFilter, bAllowFilter );
|
||||
|
||||
vg( btnDeleteNotification, column.column_type == Column.TYPE_NOTIFICATIONS );
|
||||
vg( llSearch, column.column_type == Column.TYPE_SEARCH );
|
||||
vg( llSearch, (column.column_type == Column.TYPE_SEARCH || column.column_type == Column.TYPE_SEARCH_PORTAL ) );
|
||||
vg( cbResolve, (column.column_type == Column.TYPE_SEARCH ) );
|
||||
|
||||
// tvRegexFilterErrorの表示を更新
|
||||
if( bAllowFilter ){
|
||||
|
@ -307,12 +315,32 @@ class ColumnViewHolder
|
|||
}
|
||||
|
||||
switch( column.column_type ){
|
||||
default:
|
||||
swipyRefreshLayout.setEnabled( true );
|
||||
swipyRefreshLayout.setDirection( SwipyRefreshLayoutDirection.BOTH );
|
||||
break;
|
||||
|
||||
case Column.TYPE_CONVERSATION:
|
||||
case Column.TYPE_SEARCH:
|
||||
swipyRefreshLayout.setEnabled( false );
|
||||
break;
|
||||
default:
|
||||
|
||||
case Column.TYPE_SEARCH_PORTAL:
|
||||
swipyRefreshLayout.setEnabled( true );
|
||||
swipyRefreshLayout.setDirection( SwipyRefreshLayoutDirection.BOTTOM );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
switch( column.column_type ){
|
||||
default:
|
||||
tvSearchDesc.setVisibility( View.GONE );
|
||||
break;
|
||||
case Column.TYPE_SEARCH:
|
||||
showSearchDesc( activity.getString( R.string.search_desc_mastodon_api ) );
|
||||
break;
|
||||
case Column.TYPE_SEARCH_PORTAL:
|
||||
showSearchDesc( getSearchDescPortal() );
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -331,6 +359,27 @@ class ColumnViewHolder
|
|||
}
|
||||
}
|
||||
|
||||
private String getSearchDescPortal(){
|
||||
String language_code = activity.getString( R.string.language_code );
|
||||
int res_id;
|
||||
if( "ja".equals( language_code ) ){
|
||||
res_id = R.raw.search_desc_portal_ja;
|
||||
}else{
|
||||
res_id = R.raw.search_desc_portal_en;
|
||||
}
|
||||
byte[] data = Utils.loadRawResource(activity,res_id);
|
||||
return data == null ? null : Utils.decodeUTF8( data );
|
||||
}
|
||||
|
||||
private void showSearchDesc( String html ){
|
||||
if( column==null) return;
|
||||
log.d("showSearchDesc: html=%s",html);
|
||||
tvSearchDesc.setVisibility( View.VISIBLE );
|
||||
tvSearchDesc.setMovementMethod( MyLinkMovementMethod.getInstance() );
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( column.access_info, html, false, null );
|
||||
tvSearchDesc.setText( sv );
|
||||
}
|
||||
|
||||
void showColumnColor(){
|
||||
if( column == null ) return;
|
||||
|
||||
|
@ -595,7 +644,7 @@ class ColumnViewHolder
|
|||
break;
|
||||
|
||||
case R.id.btnColumnReload:
|
||||
if( column.column_type == Column.TYPE_SEARCH ){
|
||||
if( column.column_type == Column.TYPE_SEARCH || column.column_type == Column.TYPE_SEARCH_PORTAL ){
|
||||
Utils.hideKeyboard( activity, etSearch );
|
||||
etSearch.setText( column.search_query );
|
||||
cbResolve.setChecked( column.search_resolve );
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.ArrayList;
|
|||
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.dialog.DlgQRCode;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.table.UserRelation;
|
||||
|
@ -30,7 +31,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
@NonNull final ActMain activity;
|
||||
@NonNull private final SavedAccount access_info;
|
||||
@Nullable private final TootAccount who;
|
||||
@Nullable private final TootStatus status;
|
||||
@Nullable private final TootStatusLike status;
|
||||
@NonNull private final UserRelation relation;
|
||||
@NonNull private final Column column;
|
||||
|
||||
|
@ -43,7 +44,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
@NonNull ActMain activity
|
||||
, @NonNull Column column
|
||||
, @Nullable TootAccount who
|
||||
, @Nullable TootStatus status
|
||||
, @Nullable TootStatusLike status
|
||||
){
|
||||
this.activity = activity;
|
||||
this.column = column;
|
||||
|
@ -123,11 +124,11 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
}else{
|
||||
btnDelete.setVisibility( View.GONE );
|
||||
btnReport.setOnClickListener( this );
|
||||
if( status.application == null || TextUtils.isEmpty( status.application.name ) ){
|
||||
btnMuteApp.setVisibility( View.GONE );
|
||||
}else{
|
||||
if( status.application != null && !TextUtils.isEmpty( status.application.name ) ){
|
||||
btnMuteApp.setText( activity.getString( R.string.mute_app_of, status.application.name ) );
|
||||
btnMuteApp.setOnClickListener( this );
|
||||
}else{
|
||||
btnMuteApp.setVisibility( View.GONE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -225,11 +226,20 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
v = viewRoot.findViewById( R.id.btnCancel );
|
||||
v.setOnClickListener( this );
|
||||
|
||||
v = viewRoot.findViewById( R.id.btnBoostedBy );
|
||||
v.setOnClickListener( this );
|
||||
|
||||
v = viewRoot.findViewById( R.id.btnFavouritedBy );
|
||||
v.setOnClickListener( this );
|
||||
if( access_info.isNA() ){
|
||||
v = viewRoot.findViewById( R.id.btnBoostedBy );
|
||||
v.setVisibility( View.GONE);
|
||||
|
||||
v = viewRoot.findViewById( R.id.btnFavouritedBy );
|
||||
v.setVisibility( View.GONE);
|
||||
}else{
|
||||
v = viewRoot.findViewById( R.id.btnBoostedBy );
|
||||
v.setOnClickListener( this );
|
||||
|
||||
v = viewRoot.findViewById( R.id.btnFavouritedBy );
|
||||
v.setOnClickListener( this );
|
||||
|
||||
}
|
||||
|
||||
v = viewRoot.findViewById( R.id.btnAccountQrCode );
|
||||
v.setOnClickListener( this );
|
||||
|
@ -275,7 +285,7 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
break;
|
||||
|
||||
case R.id.btnReplyAnotherAccount:
|
||||
activity.openReplyFromAnotherAccount( access_info, status );
|
||||
activity.openReplyFromAnotherAccount( status );
|
||||
break;
|
||||
|
||||
case R.id.btnDelete:
|
||||
|
@ -293,8 +303,8 @@ class DlgContextMenu implements View.OnClickListener, View.OnLongClickListener {
|
|||
break;
|
||||
|
||||
case R.id.btnReport:
|
||||
if( status != null && who != null ){
|
||||
activity.openReportForm( access_info, who, status );
|
||||
if( who != null && status instanceof TootStatus ){
|
||||
activity.openReportForm( access_info, who, (TootStatus)status );
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ class ItemListAdapter extends BaseAdapter implements AdapterView.OnItemClickList
|
|||
}else{
|
||||
holder = (ItemViewHolder) view.getTag();
|
||||
}
|
||||
holder.bind( o, position );
|
||||
holder.bind( o );
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import jp.juggler.subwaytooter.api.entity.TootDomainBlock;
|
|||
import jp.juggler.subwaytooter.api.entity.TootGap;
|
||||
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.table.AcctColor;
|
||||
import jp.juggler.subwaytooter.table.ContentWarning;
|
||||
import jp.juggler.subwaytooter.table.MediaShown;
|
||||
|
@ -79,18 +81,17 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
private final TextView tvApplication;
|
||||
|
||||
private TootStatus status;
|
||||
private TootStatusLike status;
|
||||
private TootAccount account_thumbnail;
|
||||
private TootAccount account_boost;
|
||||
private TootAccount account_follow;
|
||||
private String search_tag;
|
||||
private TootGap gap;
|
||||
private TootDomainBlock domain_block;
|
||||
private int position;
|
||||
|
||||
private final boolean bSimpleList;
|
||||
|
||||
ItemViewHolder( ActMain arg_activity, Column column, ItemListAdapter list_adapter, View view ,boolean bSimpleList ){
|
||||
ItemViewHolder( ActMain arg_activity, Column column, ItemListAdapter list_adapter, View view, boolean bSimpleList ){
|
||||
this.activity = arg_activity;
|
||||
this.column = column;
|
||||
this.access_info = column.access_info;
|
||||
|
@ -101,7 +102,6 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
this.tvFollowerName = (TextView) view.findViewById( R.id.tvFollowerName );
|
||||
this.tvBoosted = (TextView) view.findViewById( R.id.tvBoosted );
|
||||
|
||||
|
||||
if( activity.timeline_font != null ){
|
||||
Utils.scanView( view, new Utils.ScanViewCallback() {
|
||||
@Override public void onScanView( View v ){
|
||||
|
@ -111,7 +111,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
}else if( v instanceof TextView ){
|
||||
( (TextView) v ).setTypeface( activity.timeline_font );
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
this.tvContent = (MyTextView) view.findViewById( R.id.tvContent );
|
||||
this.tvMentions = (MyTextView) view.findViewById( R.id.tvMentions );
|
||||
|
||||
this.buttons_for_status = bSimpleList ? null : new StatusButtons( activity, column, view , false );
|
||||
this.buttons_for_status = bSimpleList ? null : new StatusButtons( activity, column, view, false );
|
||||
|
||||
this.flMedia = view.findViewById( R.id.flMedia );
|
||||
this.btnShowMedia = view.findViewById( R.id.btnShowMedia );
|
||||
|
@ -193,8 +193,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
lp.height = activity.app_state.media_thumb_height;
|
||||
}
|
||||
|
||||
void bind( Object item, int position ){
|
||||
this.position = position;
|
||||
void bind( Object item ){
|
||||
this.status = null;
|
||||
this.account_thumbnail = null;
|
||||
this.account_boost = null;
|
||||
|
@ -210,7 +209,9 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
if( item == null ) return;
|
||||
|
||||
if( item instanceof String ){
|
||||
if( item instanceof MSPToot ){
|
||||
showStatus( activity, (MSPToot) item );
|
||||
}else if( item instanceof String ){
|
||||
showSearchTag( (String) item );
|
||||
}else if( item instanceof TootAccount ){
|
||||
showFollow( (TootAccount) item );
|
||||
|
@ -270,7 +271,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
}else if( item instanceof TootGap ){
|
||||
showGap( (TootGap) item );
|
||||
}else if( item instanceof TootDomainBlock ){
|
||||
showDomainBlock( (TootDomainBlock)item );
|
||||
showDomainBlock( (TootDomainBlock) item );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,24 +317,30 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
Styler.setFollowIcon( activity, btnFollow, ivFollowedBy, relation );
|
||||
}
|
||||
|
||||
private void showStatus( @NonNull ActMain activity, @NonNull TootStatus status ){
|
||||
private void showStatus( @NonNull ActMain activity, @NonNull TootStatusLike status ){
|
||||
this.status = status;
|
||||
llStatus.setVisibility( View.VISIBLE );
|
||||
|
||||
tvTime.setText( TootStatus.formatTime( status.time_created_at ) );
|
||||
if( status instanceof TootStatus ){
|
||||
tvTime.setText( TootStatus.formatTime( ( (TootStatus) status ).time_created_at ) );
|
||||
|
||||
}else if( status instanceof MSPToot ){
|
||||
tvTime.setText( ( (MSPToot) status ).created_at );
|
||||
|
||||
}
|
||||
ivThumbnail.setCornerRadius( activity.pref, 16f );
|
||||
|
||||
account_thumbnail = status.account;
|
||||
setAcct( tvAcct, access_info.getFullAcct( status.account ), R.attr.colorAcctSmall );
|
||||
setAcct( tvAcct, access_info.getFullAcct( status.account ), R.attr.colorAcctSmall );
|
||||
|
||||
if(status.account == null ){
|
||||
if( status.account == null ){
|
||||
tvName.setText( "?" );
|
||||
ivThumbnail.setImageUrl(null,null);
|
||||
ivThumbnail.setImageUrl( null, null );
|
||||
}else{
|
||||
tvName.setText( status.account.display_name );
|
||||
ivThumbnail.setImageUrl(
|
||||
access_info.supplyBaseUrl( status.account.avatar_static )
|
||||
,access_info.supplyBaseUrl( status.account.avatar )
|
||||
, access_info.supplyBaseUrl( status.account.avatar )
|
||||
);
|
||||
}
|
||||
tvContent.setText( status.decoded_content );
|
||||
|
@ -345,11 +352,16 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
// tvTags.setText( status.decoded_tags );
|
||||
// }
|
||||
|
||||
if( status.decoded_mentions == null ){
|
||||
tvMentions.setVisibility( View.GONE );
|
||||
if( status instanceof TootStatus ){
|
||||
TootStatus ts = (TootStatus) status;
|
||||
if( ts.decoded_mentions == null ){
|
||||
tvMentions.setVisibility( View.GONE );
|
||||
}else{
|
||||
tvMentions.setVisibility( View.VISIBLE );
|
||||
tvMentions.setText( ts.decoded_mentions );
|
||||
}
|
||||
}else{
|
||||
tvMentions.setVisibility( View.VISIBLE );
|
||||
tvMentions.setText( status.decoded_mentions );
|
||||
tvMentions.setVisibility( View.GONE );
|
||||
}
|
||||
|
||||
// Content warning
|
||||
|
@ -359,28 +371,52 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
}else{
|
||||
llContentWarning.setVisibility( View.VISIBLE );
|
||||
tvContentWarning.setText( status.decoded_spoiler_text );
|
||||
boolean cw_shown = ContentWarning.isShown( access_info.host, status.id, false );
|
||||
boolean cw_shown = ContentWarning.isShown( status, false );
|
||||
showContent( cw_shown );
|
||||
}
|
||||
|
||||
if( status.media_attachments == null || status.media_attachments.isEmpty() ){
|
||||
flMedia.setVisibility( View.GONE );
|
||||
}else{
|
||||
flMedia.setVisibility( View.VISIBLE );
|
||||
setMedia( ivMedia1, status, 0 );
|
||||
setMedia( ivMedia2, status, 1 );
|
||||
setMedia( ivMedia3, status, 2 );
|
||||
setMedia( ivMedia4, status, 3 );
|
||||
|
||||
@SuppressWarnings("SimplifiableConditionalExpression")
|
||||
boolean default_shown =
|
||||
column.hide_media_default ? false :
|
||||
access_info.dont_hide_nsfw ? true :
|
||||
! status.sensitive;
|
||||
|
||||
// hide sensitive media
|
||||
boolean is_shown = MediaShown.isShown( access_info.host, status.id, default_shown );
|
||||
btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE );
|
||||
if( status instanceof TootStatus ){
|
||||
TootStatus ts = (TootStatus) status;
|
||||
if( ts.media_attachments == null || ts.media_attachments.isEmpty() ){
|
||||
flMedia.setVisibility( View.GONE );
|
||||
}else{
|
||||
flMedia.setVisibility( View.VISIBLE );
|
||||
setMedia( ivMedia1, ts, 0 );
|
||||
setMedia( ivMedia2, ts, 1 );
|
||||
setMedia( ivMedia3, ts, 2 );
|
||||
setMedia( ivMedia4, ts, 3 );
|
||||
|
||||
@SuppressWarnings("SimplifiableConditionalExpression")
|
||||
boolean default_shown =
|
||||
column.hide_media_default ? false :
|
||||
access_info.dont_hide_nsfw ? true :
|
||||
! status.sensitive;
|
||||
|
||||
// hide sensitive media
|
||||
boolean is_shown = MediaShown.isShown( status, default_shown );
|
||||
btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE );
|
||||
}
|
||||
}else if( status instanceof MSPToot ){
|
||||
MSPToot ts = (MSPToot) status;
|
||||
if( ts.media_attachments == null || ts.media_attachments.isEmpty() ){
|
||||
flMedia.setVisibility( View.GONE );
|
||||
}else{
|
||||
flMedia.setVisibility( View.VISIBLE );
|
||||
setMedia( ivMedia1, ts, 0 );
|
||||
setMedia( ivMedia2, ts, 1 );
|
||||
setMedia( ivMedia3, ts, 2 );
|
||||
setMedia( ivMedia4, ts, 3 );
|
||||
|
||||
@SuppressWarnings("SimplifiableConditionalExpression")
|
||||
boolean default_shown =
|
||||
column.hide_media_default ? false :
|
||||
access_info.dont_hide_nsfw ? true :
|
||||
! status.sensitive;
|
||||
|
||||
// hide sensitive media
|
||||
boolean is_shown = MediaShown.isShown( status, default_shown );
|
||||
btnShowMedia.setVisibility( ! is_shown ? View.VISIBLE : View.GONE );
|
||||
}
|
||||
}
|
||||
|
||||
if( buttons_for_status != null ){
|
||||
|
@ -432,11 +468,11 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
}else{
|
||||
iv.setVisibility( View.VISIBLE );
|
||||
iv.setScaleType( activity.dont_crop_media_thumbnail ? ImageView.ScaleType.FIT_CENTER : ImageView.ScaleType.CENTER_CROP );
|
||||
|
||||
|
||||
TootAttachment ta = status.media_attachments.get( idx );
|
||||
|
||||
if( TextUtils.isEmpty( ta.type )){
|
||||
iv.setMediaType(0);
|
||||
|
||||
if( TextUtils.isEmpty( ta.type ) ){
|
||||
iv.setMediaType( 0 );
|
||||
}else{
|
||||
switch( ta.type ){
|
||||
default:
|
||||
|
@ -463,22 +499,36 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
url = ta.url;
|
||||
}
|
||||
}
|
||||
iv.setCornerRadius( activity.pref,0f ); // 正方形じゃないせいか、うまく動かない activity.density * 4f );
|
||||
iv.setCornerRadius( activity.pref, 0f ); // 正方形じゃないせいか、うまく動かない activity.density * 4f );
|
||||
iv.setImageUrl( access_info.supplyBaseUrl( url ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void setMedia( MyNetworkImageView iv, MSPToot msp_toot, int idx ){
|
||||
if( idx >= msp_toot.media_attachments.size() ){
|
||||
iv.setVisibility( View.GONE );
|
||||
}else{
|
||||
iv.setVisibility( View.VISIBLE );
|
||||
iv.setScaleType( activity.dont_crop_media_thumbnail ? ImageView.ScaleType.FIT_CENTER : ImageView.ScaleType.CENTER_CROP );
|
||||
|
||||
String url = msp_toot.media_attachments.get( idx );
|
||||
iv.setMediaType( 0 );
|
||||
iv.setCornerRadius( activity.pref, 0f ); // 正方形じゃないせいか、うまく動かない activity.density * 4f );
|
||||
iv.setImageUrl( access_info.supplyBaseUrl( url ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onClick( View v ){
|
||||
|
||||
int pos = activity.nextPosition( column ) ;
|
||||
int pos = activity.nextPosition( column );
|
||||
|
||||
switch( v.getId() ){
|
||||
case R.id.btnHideMedia:
|
||||
MediaShown.save( access_info.host, status.id, false );
|
||||
MediaShown.save( status, false );
|
||||
btnShowMedia.setVisibility( View.VISIBLE );
|
||||
break;
|
||||
case R.id.btnShowMedia:
|
||||
MediaShown.save( access_info.host, status.id, true );
|
||||
MediaShown.save( status, true );
|
||||
btnShowMedia.setVisibility( View.GONE );
|
||||
break;
|
||||
case R.id.ivMedia1:
|
||||
|
@ -495,7 +545,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
break;
|
||||
case R.id.btnContentWarning:{
|
||||
boolean new_shown = ( llContents.getVisibility() == View.GONE );
|
||||
ContentWarning.save( access_info.host, status.id, new_shown );
|
||||
ContentWarning.save( status, new_shown );
|
||||
list_adapter.notifyDataSetChanged();
|
||||
break;
|
||||
}
|
||||
|
@ -504,7 +554,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
if( access_info.isPseudo() ){
|
||||
new DlgContextMenu( activity, column, account_thumbnail, null ).show();
|
||||
}else{
|
||||
activity.openProfile( pos,access_info, account_thumbnail );
|
||||
activity.openProfile( pos, access_info, account_thumbnail );
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -512,14 +562,14 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
if( access_info.isPseudo() ){
|
||||
new DlgContextMenu( activity, column, account_boost, null ).show();
|
||||
}else{
|
||||
activity.openProfile( pos,access_info, account_boost );
|
||||
activity.openProfile( pos, access_info, account_boost );
|
||||
}
|
||||
break;
|
||||
case R.id.llFollow:
|
||||
if( access_info.isPseudo() ){
|
||||
new DlgContextMenu( activity, column, account_follow, null ).show();
|
||||
}else{
|
||||
activity.openProfile( pos,access_info, account_follow );
|
||||
activity.openProfile( pos, access_info, account_follow );
|
||||
}
|
||||
break;
|
||||
case R.id.btnFollow:
|
||||
|
@ -528,13 +578,13 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
case R.id.btnSearchTag:
|
||||
if( search_tag != null ){
|
||||
activity.openHashTag( activity.nextPosition( column ),access_info, search_tag );
|
||||
activity.openHashTag( activity.nextPosition( column ), access_info, search_tag );
|
||||
}else if( gap != null ){
|
||||
column.startGap( gap );
|
||||
}else if( domain_block != null ){
|
||||
final String domain = domain_block.domain;
|
||||
final String domain = domain_block.domain;
|
||||
new AlertDialog.Builder( activity )
|
||||
.setMessage( activity.getString( R.string.confirm_unblock_domain, domain) )
|
||||
.setMessage( activity.getString( R.string.confirm_unblock_domain, domain ) )
|
||||
.setNegativeButton( R.string.cancel, null )
|
||||
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override public void onClick( DialogInterface dialog, int which ){
|
||||
|
@ -544,7 +594,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
.show();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,22 +616,28 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
private void clickMedia( int i ){
|
||||
try{
|
||||
TootAttachment a = status.media_attachments.get( i );
|
||||
|
||||
String sv;
|
||||
if( Pref.pref( activity ).getBoolean( Pref.KEY_PRIOR_LOCAL_URL, false ) ){
|
||||
sv = a.url;
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
sv = a.remote_url;
|
||||
}
|
||||
}else{
|
||||
sv = a.remote_url;
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
if( status instanceof MSPToot ){
|
||||
activity.openStatus( activity.nextPosition( column ), access_info, status );
|
||||
}else if( status instanceof TootStatus ){
|
||||
TootStatus ts = (TootStatus) status;
|
||||
|
||||
TootAttachment a = ts.media_attachments.get( i );
|
||||
|
||||
String sv;
|
||||
if( Pref.pref( activity ).getBoolean( Pref.KEY_PRIOR_LOCAL_URL, false ) ){
|
||||
sv = a.url;
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
sv = a.remote_url;
|
||||
}
|
||||
}else{
|
||||
sv = a.remote_url;
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
sv = a.url;
|
||||
}
|
||||
}
|
||||
int pos = activity.nextPosition( column );
|
||||
activity.openChromeTab( pos, access_info, sv, false );
|
||||
}
|
||||
int pos = activity.nextPosition( column ) ;
|
||||
activity.openChromeTab(pos, access_info, sv, false );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
@ -590,11 +646,9 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
|||
// 簡略ビューの時だけ呼ばれる
|
||||
// StatusButtonsPopupを表示する
|
||||
void onItemClick( MyListView listView, View anchor ){
|
||||
if( status != null ){
|
||||
activity.closeListItemPopup();
|
||||
activity.list_item_popup = new StatusButtonsPopup( activity, column ,bSimpleList);
|
||||
activity.list_item_popup.show( listView, anchor, status );
|
||||
}
|
||||
activity.closeListItemPopup();
|
||||
activity.list_item_popup = new StatusButtonsPopup( activity, column, bSimpleList );
|
||||
activity.list_item_popup.show( listView, anchor, status );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import android.preference.PreferenceManager;
|
|||
|
||||
public class Pref {
|
||||
|
||||
static SharedPreferences pref( Context context ){
|
||||
|
||||
public static SharedPreferences pref( Context context ){
|
||||
return PreferenceManager.getDefaultSharedPreferences( context );
|
||||
}
|
||||
|
||||
|
@ -60,6 +61,8 @@ public class Pref {
|
|||
|
||||
static final String KEY_CLIENT_NAME = "client_name";
|
||||
|
||||
public static final String KEY_MASTODON_SEARCH_PORTAL_USER_TOKEN = "mastodon_search_portal_user_token";
|
||||
|
||||
|
||||
// 項目を追加したらAppDataExporter#importPref のswitch文も更新すること
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ public class PrefDevice {
|
|||
|
||||
private static String file_name = "device";
|
||||
|
||||
static SharedPreferences prefDevice( Context context ){
|
||||
public static SharedPreferences prefDevice( Context context ){
|
||||
return context.getSharedPreferences( file_name, Context.MODE_PRIVATE );
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import android.widget.ImageView;
|
|||
import android.widget.PopupWindow;
|
||||
|
||||
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.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.table.UserRelation;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
@ -27,7 +29,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
private final ImageView ivFollowedBy2;
|
||||
private final View llFollow2;
|
||||
|
||||
final boolean bSimpleList;
|
||||
private final boolean bSimpleList;
|
||||
|
||||
StatusButtons( @NonNull ActMain activity, @NonNull Column column, @NonNull View viewRoot ,boolean bSimpleList){
|
||||
this.activity = activity;
|
||||
|
@ -63,32 +65,40 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
}
|
||||
|
||||
private TootStatus status;
|
||||
private UserRelation relation;
|
||||
private TootStatusLike status;
|
||||
|
||||
void bind( @NonNull TootStatus status ){
|
||||
void bind( @NonNull TootStatusLike status ){
|
||||
this.status = status;
|
||||
|
||||
int color_normal = Styler.getAttributeColor( activity, R.attr.colorImageButton );
|
||||
int color_accent = Styler.getAttributeColor( activity, R.attr.colorImageButtonAccent );
|
||||
|
||||
if( TootStatus.VISIBILITY_DIRECT.equals( status.visibility ) ){
|
||||
setButton( btnBoost, false, color_accent, R.attr.ic_mail, "" );
|
||||
}else if( TootStatus.VISIBILITY_PRIVATE.equals( status.visibility ) ){
|
||||
setButton( btnBoost, false, color_accent, R.attr.ic_lock, "" );
|
||||
}else if( activity.app_state.isBusyBoost( access_info, status ) ){
|
||||
setButton( btnBoost, false, color_normal, R.attr.btn_refresh, "?" );
|
||||
}else{
|
||||
int color = ( status.reblogged ? color_accent : color_normal );
|
||||
setButton( btnBoost, true, color, R.attr.btn_boost, Long.toString( status.reblogs_count ) );
|
||||
if( status instanceof MSPToot ){
|
||||
setButton( btnBoost, true, color_normal, R.attr.btn_boost, "" );
|
||||
setButton( btnFavourite, true, color_normal, R.attr.btn_favourite, "");
|
||||
}else if( status instanceof TootStatus ){
|
||||
TootStatus ts = (TootStatus)status;
|
||||
|
||||
if( TootStatus.VISIBILITY_DIRECT.equals( ts.visibility ) ){
|
||||
setButton( btnBoost, false, color_accent, R.attr.ic_mail, "" );
|
||||
}else if( TootStatus.VISIBILITY_PRIVATE.equals( ts.visibility ) ){
|
||||
setButton( btnBoost, false, color_accent, R.attr.ic_lock, "" );
|
||||
}else if( activity.app_state.isBusyBoost( access_info, status ) ){
|
||||
setButton( btnBoost, false, color_normal, R.attr.btn_refresh, "?" );
|
||||
}else{
|
||||
int color = ( ts.reblogged ? color_accent : color_normal );
|
||||
setButton( btnBoost, true, color, R.attr.btn_boost, Long.toString( ts.reblogs_count ) );
|
||||
}
|
||||
|
||||
if( activity.app_state.isBusyFav( access_info, status ) ){
|
||||
setButton( btnFavourite, false, color_normal, R.attr.btn_refresh, "?" );
|
||||
}else{
|
||||
int color = ( ts.favourited ? color_accent : color_normal );
|
||||
setButton( btnFavourite, true, color, R.attr.btn_favourite, Long.toString( ts.favourites_count ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( activity.app_state.isBusyFav( access_info, status ) ){
|
||||
setButton( btnFavourite, false, color_normal, R.attr.btn_refresh, "?" );
|
||||
}else{
|
||||
int color = ( status.favourited ? color_accent : color_normal );
|
||||
setButton( btnFavourite, true, color, R.attr.btn_favourite, Long.toString( status.favourites_count ) );
|
||||
}
|
||||
|
||||
if( status.account == null || ! activity.pref.getBoolean( Pref.KEY_SHOW_FOLLOW_BUTTON_IN_BUTTON_BAR, false ) ){
|
||||
llFollow2.setVisibility( View.GONE );
|
||||
|
@ -101,6 +111,7 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
|
||||
}
|
||||
|
||||
|
||||
private void setButton( Button b, boolean enabled, int color, int icon_attr, String text ){
|
||||
Drawable d = Styler.getAttributeDrawable( activity, icon_attr ).mutate();
|
||||
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
|
||||
|
@ -119,12 +130,13 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
activity.openStatus( activity.nextPosition( column ), access_info, status );
|
||||
break;
|
||||
case R.id.btnReply:
|
||||
if( access_info.isPseudo() ){
|
||||
activity.openReplyFromAnotherAccount( access_info, status );
|
||||
if( status instanceof TootStatus && !access_info.isPseudo() ){
|
||||
activity.performReply( access_info, (TootStatus)status );
|
||||
}else{
|
||||
activity.performReply( access_info, status ,false);
|
||||
activity.openReplyFromAnotherAccount( status );
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.btnBoost:
|
||||
if( access_info.isPseudo() ){
|
||||
activity.openBoostFromAnotherAccount( access_info, status );
|
||||
|
@ -159,8 +171,10 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
break;
|
||||
|
||||
case R.id.btnFollow2:
|
||||
if( access_info.isPseudo() ){
|
||||
activity.openFollowFromAnotherAccount( access_info, status );
|
||||
if( status == null || status.account == null ){
|
||||
// 何もしない
|
||||
}else if( access_info.isPseudo() ){
|
||||
activity.openFollowFromAnotherAccount( access_info, status.account );
|
||||
}else if( relation.blocking || relation.muting ){
|
||||
// 何もしない
|
||||
}else if( relation.following || relation.requested ){
|
||||
|
@ -184,11 +198,13 @@ class StatusButtons implements View.OnClickListener, View.OnLongClickListener {
|
|||
break;
|
||||
|
||||
case R.id.btnFollow2:
|
||||
activity.openFollowFromAnotherAccount( access_info, status );
|
||||
if( status != null && status.account != null ){
|
||||
activity.openFollowFromAnotherAccount( access_info, status.account );
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.btnReply:
|
||||
activity.openReplyFromAnotherAccount( access_info, status );
|
||||
activity.openReplyFromAnotherAccount( status );
|
||||
break;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ import android.view.Gravity;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
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.view.MyListView;
|
||||
|
||||
class StatusButtonsPopup {
|
||||
|
@ -34,7 +35,7 @@ class StatusButtonsPopup {
|
|||
}
|
||||
}
|
||||
|
||||
void show( final MyListView listView, View anchor, TootStatus status ){
|
||||
void show( final MyListView listView, View anchor, TootStatusLike status ){
|
||||
|
||||
//
|
||||
window = new PopupWindow( activity );
|
||||
|
|
|
@ -119,9 +119,9 @@ class StreamReader {
|
|||
private Object parsePayload( String event, JSONObject obj ){
|
||||
try{
|
||||
if( "update".equals( event ) ){
|
||||
return TootStatus.parse( log, access_info, new JSONObject( obj.optString( "payload" ) ) );
|
||||
return TootStatus.parse( log, access_info, access_info.host,new JSONObject( obj.optString( "payload" ) ) );
|
||||
}else if( "notification".equals( event ) ){
|
||||
return TootNotification.parse( log, access_info, new JSONObject( obj.optString( "payload" ) ) );
|
||||
return TootNotification.parse( log, access_info, access_info.host,new JSONObject( obj.optString( "payload" ) ) );
|
||||
}else if( "delete".equals( event ) ){
|
||||
return obj.optLong( "payload", - 1L );
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ public class TootAccount {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static CharSequence filterDisplayName( String sv ){
|
||||
public static CharSequence filterDisplayName( String sv ){
|
||||
|
||||
// decode HTML entity
|
||||
sv = HTMLDecoder.decodeEntity(sv );
|
||||
|
@ -155,4 +155,14 @@ public class TootAccount {
|
|||
return Emojione.decodeEmoji( sv ) ;
|
||||
}
|
||||
|
||||
public String getAcctHost(){
|
||||
try{
|
||||
int pos = acct.indexOf( '@' );
|
||||
if( pos != - 1 ) return acct.substring( pos + 1 );
|
||||
}catch(Throwable ignored){
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ public class TootContext {
|
|||
// descendants The descendants of the status in the conversation, as a list of Statuses
|
||||
public TootStatus.List descendants;
|
||||
|
||||
public static TootContext parse( LogCategory log, LinkClickContext account,JSONObject src ){
|
||||
public static TootContext parse( LogCategory log, LinkClickContext lcc,String status_host,JSONObject src ){
|
||||
if( src==null) return null;
|
||||
try{
|
||||
TootContext dst = new TootContext();
|
||||
dst.ancestors = TootStatus.parseList( log, account,src.optJSONArray( "ancestors" ) );
|
||||
dst.descendants = TootStatus.parseList(log, account, src.optJSONArray( "descendants" ) );
|
||||
dst.ancestors = TootStatus.parseList( log, lcc,status_host,src.optJSONArray( "ancestors" ) );
|
||||
dst.descendants = TootStatus.parseList(log, lcc, status_host,src.optJSONArray( "descendants" ) );
|
||||
return dst;
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
|
|
|
@ -37,7 +37,7 @@ public class TootNotification extends TootId {
|
|||
|
||||
public JSONObject json;
|
||||
|
||||
public static TootNotification parse( LogCategory log, LinkClickContext accopunt, JSONObject src ){
|
||||
public static TootNotification parse( LogCategory log, LinkClickContext lcc,String status_host, JSONObject src ){
|
||||
if( src == null ) return null;
|
||||
try{
|
||||
TootNotification dst = new TootNotification();
|
||||
|
@ -45,8 +45,8 @@ public class TootNotification extends TootId {
|
|||
dst.id = src.optLong( "id" );
|
||||
dst.type = Utils.optStringX( src, "type" );
|
||||
dst.created_at = Utils.optStringX( src, "created_at" );
|
||||
dst.account = TootAccount.parse( log, accopunt, src.optJSONObject( "account" ) );
|
||||
dst.status = TootStatus.parse( log, accopunt, src.optJSONObject( "status" ) );
|
||||
dst.account = TootAccount.parse( log, lcc, src.optJSONObject( "account" ) );
|
||||
dst.status = TootStatus.parse( log, lcc, status_host,src.optJSONObject( "status" ) );
|
||||
|
||||
dst.time_created_at = TootStatus.parseTime( log, dst.created_at );
|
||||
|
||||
|
@ -69,7 +69,7 @@ public class TootNotification extends TootId {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
public static List parseList( LogCategory log, LinkClickContext account, JSONArray array ){
|
||||
public static List parseList( LogCategory log, LinkClickContext lcc,String status_host, JSONArray array ){
|
||||
List result = new List();
|
||||
if( array != null ){
|
||||
int array_size = array.length();
|
||||
|
@ -77,7 +77,7 @@ public class TootNotification extends TootId {
|
|||
for( int i = 0 ; i < array_size ; ++ i ){
|
||||
JSONObject src = array.optJSONObject( i );
|
||||
if( src == null ) continue;
|
||||
TootNotification item = parse( log, account, src );
|
||||
TootNotification item = parse( log, lcc,status_host, src );
|
||||
if( item != null ) result.add( item );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ public class TootResults {
|
|||
// An array of matched hashtags, as strings
|
||||
public ArrayList< String > hashtags;
|
||||
|
||||
public static TootResults parse( LogCategory log, LinkClickContext account, JSONObject src ){
|
||||
public static TootResults parse( LogCategory log, LinkClickContext lcc,String status_host, JSONObject src ){
|
||||
try{
|
||||
if( src == null ) return null;
|
||||
TootResults dst = new TootResults();
|
||||
dst.accounts = TootAccount.parseList( log, account, src.optJSONArray( "accounts" ) );
|
||||
dst.statuses = TootStatus.parseList( log, account, src.optJSONArray( "statuses" ) );
|
||||
dst.accounts = TootAccount.parseList( log, lcc, src.optJSONArray( "accounts" ) );
|
||||
dst.statuses = TootStatus.parseList( log, lcc, status_host,src.optJSONArray( "statuses" ) );
|
||||
dst.hashtags = Utils.parseStringArray( log, src.optJSONArray( "hashtags" ) );
|
||||
return dst;
|
||||
}catch( Throwable ex ){
|
||||
|
|
|
@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.api.entity;
|
|||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
|
@ -19,13 +20,14 @@ import java.util.TimeZone;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
import jp.juggler.subwaytooter.util.LinkClickContext;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree;
|
||||
|
||||
public class TootStatus extends TootId {
|
||||
public class TootStatus extends TootStatusLike {
|
||||
|
||||
public static class List extends ArrayList< TootStatus > {
|
||||
|
||||
|
@ -44,12 +46,9 @@ public class TootStatus extends TootId {
|
|||
// A Fediverse-unique resource ID
|
||||
public String uri;
|
||||
|
||||
//URL to the status page (can be remote)
|
||||
public String url;
|
||||
|
||||
// The TootAccount which posted the status
|
||||
@Nullable public TootAccount account;
|
||||
|
||||
|
||||
|
||||
// null or the ID of the status it replies to
|
||||
public String in_reply_to_id;
|
||||
|
||||
|
@ -58,31 +57,12 @@ public class TootStatus extends TootId {
|
|||
|
||||
// null or the reblogged Status
|
||||
public TootStatus reblog;
|
||||
|
||||
// Body of the status; this will contain HTML (remote HTML already sanitized)
|
||||
public String content;
|
||||
|
||||
|
||||
// The time the status was created
|
||||
public String created_at;
|
||||
|
||||
//The number of reblogs for the status
|
||||
public long reblogs_count;
|
||||
|
||||
//The number of favourites for the status
|
||||
public long favourites_count;
|
||||
|
||||
// Whether the authenticated user has reblogged the status
|
||||
public boolean reblogged;
|
||||
|
||||
// Whether the authenticated user has favourited the status
|
||||
public boolean favourited;
|
||||
|
||||
//Whether media attachments should be hidden by default
|
||||
public boolean sensitive;
|
||||
|
||||
//If not empty, warning text that should be displayed before the actual content
|
||||
public String spoiler_text;
|
||||
|
||||
|
||||
|
||||
//One of: public, unlisted, private, direct
|
||||
public String visibility;
|
||||
public static final String VISIBILITY_PUBLIC = "public";
|
||||
|
@ -99,13 +79,9 @@ public class TootStatus extends TootId {
|
|||
//An array of Tags
|
||||
public TootTag.List tags;
|
||||
|
||||
//Application from which the status was posted
|
||||
public TootApplication application;
|
||||
|
||||
|
||||
public long time_created_at;
|
||||
|
||||
public Spannable decoded_content;
|
||||
public Spannable decoded_spoiler_text;
|
||||
// public Spannable decoded_tags;
|
||||
public Spannable decoded_mentions;
|
||||
|
||||
|
@ -113,7 +89,7 @@ public class TootStatus extends TootId {
|
|||
|
||||
public boolean conversation_main;
|
||||
|
||||
public static TootStatus parse( LogCategory log, LinkClickContext account, JSONObject src ){
|
||||
public static TootStatus parse( @NonNull LogCategory log, @NonNull LinkClickContext lcc, @NonNull String status_host, JSONObject src ){
|
||||
|
||||
if( src == null ) return null;
|
||||
|
||||
|
@ -122,12 +98,13 @@ public class TootStatus extends TootId {
|
|||
status.json = src;
|
||||
// log.d( "parse: %s", src.toString() );
|
||||
status.id = src.optLong( "id" );
|
||||
status.status_host = status_host;
|
||||
status.uri = Utils.optStringX( src, "uri" );
|
||||
status.url = Utils.optStringX( src, "url" );
|
||||
status.account = TootAccount.parse( log, account, src.optJSONObject( "account" ) );
|
||||
status.account = TootAccount.parse( log, lcc, 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, account, src.optJSONObject( "reblog" ) );
|
||||
status.reblog = TootStatus.parse( log, lcc,status_host, 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" );
|
||||
|
@ -143,12 +120,12 @@ public class TootStatus extends TootId {
|
|||
status.application = TootApplication.parse( log, src.optJSONObject( "application" ) ); // null
|
||||
|
||||
status.time_created_at = parseTime( log, status.created_at );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( account, status.content ,true,status.media_attachments );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( lcc, status.content ,true,status.media_attachments );
|
||||
// status.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
status.decoded_mentions = HTMLDecoder.decodeMentions( account, status.mentions );
|
||||
status.decoded_mentions = HTMLDecoder.decodeMentions( lcc, status.mentions );
|
||||
|
||||
if( ! TextUtils.isEmpty( status.spoiler_text ) ){
|
||||
status.decoded_spoiler_text = HTMLDecoder.decodeHTML( account, status.spoiler_text ,true,status.media_attachments);
|
||||
status.decoded_spoiler_text = HTMLDecoder.decodeHTML( lcc, status.spoiler_text ,true,status.media_attachments);
|
||||
}
|
||||
return status;
|
||||
}catch( Throwable ex ){
|
||||
|
@ -159,7 +136,7 @@ public class TootStatus extends TootId {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
public static List parseList( LogCategory log, LinkClickContext account, JSONArray array ){
|
||||
public static List parseList( @NonNull LogCategory log, @NonNull LinkClickContext lcc, @NonNull String status_host, JSONArray array ){
|
||||
List result = new List();
|
||||
if( array != null ){
|
||||
int array_size = array.length();
|
||||
|
@ -167,7 +144,7 @@ public class TootStatus extends TootId {
|
|||
for( int i = 0 ; i < array_size ; ++ i ){
|
||||
JSONObject src = array.optJSONObject( i );
|
||||
if( src == null ) continue;
|
||||
TootStatus item = parse( log, account, src );
|
||||
TootStatus item = parse( log, lcc,status_host, src );
|
||||
if( item != null ) result.add( item );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package jp.juggler.subwaytooter.api.entity;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Spannable;
|
||||
|
||||
|
||||
public abstract class TootStatusLike extends TootId{
|
||||
|
||||
//URL to the status page (can be remote)
|
||||
public String url;
|
||||
|
||||
public String status_host;
|
||||
|
||||
// The TootAccount which posted the status
|
||||
@Nullable public TootAccount account;
|
||||
|
||||
|
||||
//The number of reblogs for the status
|
||||
public long reblogs_count;
|
||||
|
||||
//The number of favourites for the status
|
||||
public long favourites_count;
|
||||
|
||||
// Whether the authenticated user has reblogged the status
|
||||
public boolean reblogged;
|
||||
|
||||
// Whether the authenticated user has favourited the status
|
||||
public boolean favourited;
|
||||
|
||||
//Whether media attachments should be hidden by default
|
||||
public boolean sensitive;
|
||||
|
||||
//If not empty, warning text that should be displayed before the actual content
|
||||
public String spoiler_text;
|
||||
public Spannable decoded_spoiler_text;
|
||||
|
||||
// Body of the status; this will contain HTML (remote HTML already sanitized)
|
||||
public String content;
|
||||
public Spannable decoded_content;
|
||||
|
||||
//Application from which the status was posted
|
||||
public TootApplication application;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package jp.juggler.subwaytooter.api_msp;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
||||
public class MSPApiResult extends TootApiResult {
|
||||
|
||||
MSPApiResult( String error ){
|
||||
super( error );
|
||||
}
|
||||
|
||||
MSPApiResult( Response response, String json, JSONArray array ){
|
||||
super(null);
|
||||
this.json = json;
|
||||
this.array = array;
|
||||
this.response = response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package jp.juggler.subwaytooter.api_msp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.Pref;
|
||||
import jp.juggler.subwaytooter.R;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class MSPClient {
|
||||
static final LogCategory log = new LogCategory("MSPClient");
|
||||
|
||||
private static final String url_token = "http://mastodonsearch.jp/api/v1.0.1/utoken";
|
||||
private static final String url_search = "http://mastodonsearch.jp/api/v1.0.1/cross";
|
||||
private static final String api_key = "e53de7f66130208f62d1808672bf6320523dcd0873dc69bc";
|
||||
|
||||
private static final OkHttpClient ok_http_client = App1.ok_http_client;
|
||||
|
||||
|
||||
public interface Callback {
|
||||
boolean isApiCancelled();
|
||||
|
||||
void publishApiProgress( String s );
|
||||
}
|
||||
|
||||
public static MSPApiResult search( @NonNull Context context, @NonNull String query, @NonNull String max_id ,@NonNull Callback callback ){
|
||||
// ユーザトークンを読む
|
||||
SharedPreferences pref = Pref.pref(context);
|
||||
String user_token = pref.getString(Pref.KEY_MASTODON_SEARCH_PORTAL_USER_TOKEN,null);
|
||||
|
||||
Response response;
|
||||
|
||||
for(;;){
|
||||
// ユーザトークンがなければ取得する
|
||||
if( TextUtils.isEmpty( user_token ) ){
|
||||
|
||||
callback.publishApiProgress( "get MSP user token..." );
|
||||
|
||||
String url = url_token + "?apikey=" + Uri.encode( api_key );
|
||||
|
||||
try{
|
||||
Request request = new Request.Builder()
|
||||
.url( url )
|
||||
.build();
|
||||
|
||||
Call call = ok_http_client.newCall( request );
|
||||
response = call.execute();
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) );
|
||||
}
|
||||
|
||||
if( callback.isApiCancelled() ) return null;
|
||||
|
||||
if( ! response.isSuccessful() ){
|
||||
if( response.code() >= 400 ){
|
||||
try{
|
||||
String json = response.body().string();
|
||||
JSONObject object = new JSONObject( json );
|
||||
JSONObject error = object.getJSONObject( "error" );
|
||||
return new MSPApiResult( String.format( "API returns error. %s: %s"
|
||||
, error.optString( "type" )
|
||||
, error.optString( "detail" )
|
||||
) );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, "API returns error response %s, but can't parse response body.", response.code() ) );
|
||||
}
|
||||
}else{
|
||||
return new MSPApiResult( context.getString( R.string.network_error_arg, response ) );
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
//noinspection ConstantConditions
|
||||
String json = response.body().string();
|
||||
JSONObject object = new JSONObject( json );
|
||||
user_token = object.getJSONObject( "result" ).getString( "token" );
|
||||
if( TextUtils.isEmpty( user_token ) ){
|
||||
return new MSPApiResult( String.format( "Can't get MSP user token. response=%s", json ) );
|
||||
}else{
|
||||
pref.edit().putString( Pref.KEY_MASTODON_SEARCH_PORTAL_USER_TOKEN, user_token ).apply();
|
||||
}
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, "API data error" ) );
|
||||
}
|
||||
}
|
||||
// ユーザトークンを使って検索APIを呼び出す
|
||||
{
|
||||
callback.publishApiProgress( "waiting search result..." );
|
||||
String url = url_search
|
||||
+ "?apikey=" + Uri.encode( api_key )
|
||||
+ "&utoken=" + Uri.encode( user_token )
|
||||
+ "&max=" + Uri.encode( max_id )
|
||||
+ "&q=" + Uri.encode( query );
|
||||
|
||||
try{
|
||||
Request request = new Request.Builder()
|
||||
.url( url )
|
||||
.build();
|
||||
|
||||
Call call = ok_http_client.newCall( request );
|
||||
response = call.execute();
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) );
|
||||
}
|
||||
|
||||
if( callback.isApiCancelled() ) return null;
|
||||
|
||||
if( ! response.isSuccessful() ){
|
||||
if( response.code() >= 400 ){
|
||||
try{
|
||||
String json = response.body().string();
|
||||
JSONObject object = new JSONObject( json );
|
||||
JSONObject error = object.getJSONObject( "error" );
|
||||
// ユーザトークンがダメなら生成しなおす
|
||||
if( "utoken".equals( error.optString( "detail" ) ) ){
|
||||
user_token = null;
|
||||
continue;
|
||||
}
|
||||
return new MSPApiResult( String.format( "API returns error. %s: %s"
|
||||
, error.optString( "type" )
|
||||
, error.optString( "detail" )
|
||||
) );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, "API returns error response %s, but can't parse response body.", response.code() ) );
|
||||
}
|
||||
}else{
|
||||
return new MSPApiResult( context.getString( R.string.network_error_arg, response ) );
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
//noinspection ConstantConditions
|
||||
String json = response.body().string();
|
||||
JSONArray array = new JSONArray( json );
|
||||
return new MSPApiResult( response, json, array );
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
return new MSPApiResult( Utils.formatError( ex, "API data error" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMaxId( JSONArray array, String max_id ){
|
||||
// max_id の更新
|
||||
int size = array.length();
|
||||
if( size > 0 ){
|
||||
JSONObject item = array.optJSONObject( size - 1 );
|
||||
if( item != null ){
|
||||
String sv = item.optString( "msp_id" );
|
||||
if( ! TextUtils.isEmpty( sv ) ){
|
||||
return sv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return max_id;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package jp.juggler.subwaytooter.api_msp.entity;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
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.api.entity.TootAccount;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
public class MSPToot extends TootStatusLike {
|
||||
|
||||
public static class List extends ArrayList<MSPToot> {
|
||||
|
||||
}
|
||||
|
||||
private static final Pattern reAccountUrl = Pattern.compile("\\Ahttps://([^/#?]+)/@([^/#?]+)\\z");
|
||||
|
||||
|
||||
private static TootAccount parseAccount( LogCategory log, SavedAccount access_info, JSONObject src ){
|
||||
|
||||
if( src == null ) return null;
|
||||
|
||||
TootAccount dst = new TootAccount();
|
||||
dst.url = Utils.optStringX( src, "url" );
|
||||
dst.username = Utils.optStringX( src, "username" );
|
||||
dst.avatar = dst.avatar_static = Utils.optStringX( src, "avatar" );
|
||||
|
||||
String sv = Utils.optStringX( src, "display_name" );
|
||||
if( TextUtils.isEmpty( sv ) ){
|
||||
dst.display_name = dst.username;
|
||||
}else{
|
||||
dst.display_name = TootAccount.filterDisplayName( sv );
|
||||
}
|
||||
|
||||
dst.id = src.optLong( "id" );
|
||||
dst.note = HTMLDecoder.decodeHTML( access_info, Utils.optStringX( src, "note" ), true, null );
|
||||
|
||||
if( TextUtils.isEmpty( dst.url ) ){
|
||||
log.e( "parseAccount: missing url" );
|
||||
return null;
|
||||
}
|
||||
Matcher m = reAccountUrl.matcher( dst.url );
|
||||
if( ! m.find() ){
|
||||
log.e( "parseAccount: not account url: %s", dst.url );
|
||||
return null;
|
||||
}else{
|
||||
dst.acct = dst.username + "@" + m.group( 1 );
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// private static final Pattern reTime = Pattern.compile( "\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)" );
|
||||
//
|
||||
// private static final TimeZone tz_tokyo = TimeZone.getTimeZone( "Asia/Tokyo" );
|
||||
//
|
||||
// private static long parseMSPTime( LogCategory log, String strTime ){
|
||||
// if( ! TextUtils.isEmpty( strTime ) ){
|
||||
// try{
|
||||
// Matcher m = reTime.matcher( strTime );
|
||||
// if( ! m.find() ){
|
||||
// log.d( "!!invalid time format: %s", strTime );
|
||||
// }else{
|
||||
// GregorianCalendar g = new GregorianCalendar( tz_tokyo );
|
||||
// g.set(
|
||||
// Utils.parse_int( m.group( 1 ), 1 ),
|
||||
// Utils.parse_int( m.group( 2 ), 1 ) - 1,
|
||||
// Utils.parse_int( m.group( 3 ), 1 ),
|
||||
// Utils.parse_int( m.group( 4 ), 0 ),
|
||||
// Utils.parse_int( m.group( 5 ), 0 ),
|
||||
// Utils.parse_int( m.group( 6 ), 0 )
|
||||
// );
|
||||
// g.set( Calendar.MILLISECOND, 0 );
|
||||
// return g.getTimeInMillis();
|
||||
// }
|
||||
// }catch( Throwable ex ){// ParseException, ArrayIndexOutOfBoundsException
|
||||
// ex.printStackTrace();
|
||||
// log.e( ex, "parseMSPTime failed. src=%s", strTime );
|
||||
// }
|
||||
// }
|
||||
// return 0L;
|
||||
// }
|
||||
|
||||
public String created_at;
|
||||
public ArrayList<String> media_attachments;
|
||||
public long msp_id;
|
||||
|
||||
private static MSPToot parse( LogCategory log, SavedAccount access_info,JSONObject src ){
|
||||
if( src == null ) return null;
|
||||
MSPToot dst = new MSPToot();
|
||||
|
||||
dst.account =parseAccount( log, access_info, src.optJSONObject( "account" ));
|
||||
if( dst.account == null ){
|
||||
log.e("missing status account");
|
||||
return null;
|
||||
}
|
||||
|
||||
dst.url = Utils.optStringX( src, "url" );
|
||||
dst.status_host = dst.account.getAcctHost();
|
||||
dst.id = src.optLong( "id" ,-1L );
|
||||
|
||||
if( TextUtils.isEmpty( dst.url ) || TextUtils.isEmpty( dst.status_host ) || dst.id == -1L ){
|
||||
log.e("missing status url or host or id");
|
||||
return null;
|
||||
}
|
||||
|
||||
dst.created_at = Utils.optStringX( src, "created_at" );
|
||||
|
||||
JSONArray a = src.optJSONArray( "media_attachments" );
|
||||
if( a != null && a.length() > 0 ){
|
||||
dst.media_attachments = new ArrayList<>();
|
||||
for(int i=0,ie=a.length();i<ie;++i){
|
||||
String sv = Utils.optStringX( a,i );
|
||||
if(!TextUtils.isEmpty( sv )){
|
||||
dst.media_attachments.add( sv );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst.msp_id = src.optLong("msp_id");
|
||||
dst.sensitive = (src.optInt( "sensitive" ,0) != 0);
|
||||
|
||||
dst.spoiler_text = Utils.optStringX( src, "spoiler_text" );
|
||||
if( ! TextUtils.isEmpty( dst.spoiler_text ) ){
|
||||
dst.decoded_spoiler_text = HTMLDecoder.decodeHTML( access_info, dst.spoiler_text ,true,null);
|
||||
}
|
||||
|
||||
dst.content = Utils.optStringX( src, "content" );
|
||||
dst.decoded_content = HTMLDecoder.decodeHTML( access_info, dst.content ,true,null );
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
public static List parseList( LogCategory log, SavedAccount access_info,JSONArray array ){
|
||||
List list = new List();
|
||||
for(int i=0,ie=array.length();i<ie;++i){
|
||||
JSONObject src = array.optJSONObject( i );
|
||||
if( src == null ) continue;
|
||||
MSPToot item = parse( log, access_info,src );
|
||||
if( item == null ) continue;
|
||||
list.add( item );
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -3,8 +3,10 @@ package jp.juggler.subwaytooter.table;
|
|||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
public class ContentWarning {
|
||||
|
@ -40,9 +42,9 @@ public class ContentWarning {
|
|||
}
|
||||
private static final String[] projection_shown = new String[]{COL_SHOWN};
|
||||
|
||||
public static boolean isShown( String host,long status_id ,boolean default_value ){
|
||||
public static boolean isShown( @NonNull TootStatusLike status , boolean default_value ){
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table, projection_shown, "h=? and si=?", new String[]{host, Long.toString(status_id) }, null, null, null );
|
||||
Cursor cursor = App1.getDB().query( table, projection_shown, "h=? and si=?", new String[]{status.status_host, Long.toString(status.id) }, null, null, null );
|
||||
try{
|
||||
if( cursor.moveToFirst() ){
|
||||
int iv = cursor.getInt( cursor.getColumnIndex( COL_SHOWN ) );
|
||||
|
@ -57,13 +59,13 @@ public class ContentWarning {
|
|||
return default_value ;
|
||||
}
|
||||
|
||||
public static void save( String host,long status_id ,boolean is_shown ){
|
||||
public static void save( @NonNull TootStatusLike status ,boolean is_shown ){
|
||||
try{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put( COL_HOST, host );
|
||||
cv.put( COL_STATUS_ID, status_id );
|
||||
cv.put( COL_HOST, status.status_host );
|
||||
cv.put( COL_STATUS_ID, status.id );
|
||||
cv.put( COL_SHOWN, is_shown ? 1:0 );
|
||||
cv.put( COL_TIME_SAVE, now );
|
||||
App1.getDB().replace( table, null, cv );
|
||||
|
|
|
@ -3,8 +3,10 @@ package jp.juggler.subwaytooter.table;
|
|||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
public class MediaShown {
|
||||
|
@ -41,9 +43,9 @@ public class MediaShown {
|
|||
|
||||
private static final String[] projection_shown = new String[]{COL_SHOWN};
|
||||
|
||||
public static boolean isShown( String host,long status_id ,boolean default_value ){
|
||||
public static boolean isShown( @NonNull TootStatusLike status , boolean default_value ){
|
||||
try{
|
||||
Cursor cursor = App1.getDB().query( table, projection_shown, "h=? and si=?", new String[]{host, Long.toString(status_id) }, null, null, null );
|
||||
Cursor cursor = App1.getDB().query( table, projection_shown, "h=? and si=?", new String[]{ status.status_host, Long.toString(status.id) }, null, null, null );
|
||||
try{
|
||||
if( cursor.moveToFirst() ){
|
||||
return ( 0 != cursor.getInt( cursor.getColumnIndex( COL_SHOWN ) ) );
|
||||
|
@ -57,13 +59,13 @@ public class MediaShown {
|
|||
return default_value ;
|
||||
}
|
||||
|
||||
public static void save( String host,long status_id ,boolean is_shown ){
|
||||
public static void save( @NonNull TootStatusLike status ,boolean is_shown ){
|
||||
try{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put( COL_HOST, host );
|
||||
cv.put( COL_STATUS_ID, status_id );
|
||||
cv.put( COL_HOST, status.status_host );
|
||||
cv.put( COL_STATUS_ID, status.id );
|
||||
cv.put( COL_SHOWN, is_shown ? 1:0 );
|
||||
cv.put( COL_TIME_SAVE, now );
|
||||
App1.getDB().replace( table, null, cv );
|
||||
|
|
|
@ -191,6 +191,30 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
|
||||
private SavedAccount(){
|
||||
}
|
||||
|
||||
// 横断検索用の、何とも紐ついていないアカウント
|
||||
// 保存しない。
|
||||
private static SavedAccount na_account;
|
||||
public static SavedAccount getNA(){
|
||||
if( na_account == null ){
|
||||
SavedAccount dst = new SavedAccount();
|
||||
dst.db_id = - 1L;
|
||||
dst.username = "?";
|
||||
dst.acct = "?@?";
|
||||
dst.host = "?";
|
||||
dst.notification_follow = false;
|
||||
dst.notification_favourite = false;
|
||||
dst.notification_boost = false;
|
||||
dst.notification_mention = false;
|
||||
na_account = dst;
|
||||
}
|
||||
return na_account;
|
||||
}
|
||||
public boolean isNA(){
|
||||
return acct.equals( "?@?" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static SavedAccount parse( Cursor cursor ) throws JSONException{
|
||||
JSONObject src = new JSONObject( cursor.getString( cursor.getColumnIndex( COL_ACCOUNT ) ) );
|
||||
|
@ -475,4 +499,5 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
|||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ public class UserRelation {
|
|||
public boolean muting;
|
||||
public boolean requested;
|
||||
|
||||
private UserRelation(){
|
||||
public UserRelation(){
|
||||
}
|
||||
|
||||
private static final LruCache< String, UserRelation > mMemoryCache = new LruCache<>( 2048 );
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.ContentResolver;
|
|||
import android.content.Context;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -1107,4 +1108,23 @@ public class Utils {
|
|||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] loadRawResource(Context context,int res_id){
|
||||
try{
|
||||
InputStream is = context.getResources().openRawResource( res_id );
|
||||
try{
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
IOUtils.copy( is, bao );
|
||||
|
||||
return bao.toByteArray();
|
||||
|
||||
}finally{
|
||||
IOUtils.closeQuietly( is );
|
||||
}
|
||||
|
||||
}catch( Throwable ex ){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,17 @@
|
|||
</LinearLayout>
|
||||
</jp.juggler.subwaytooter.view.MaxHeightScrollView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="0dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="3dp"
|
||||
android:background="?attr/colorSearchFormBackground"
|
||||
android:id="@+id/tvSearchDesc"
|
||||
/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/llSearch"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -92,6 +92,16 @@
|
|||
<!--android:title="Tools"/>-->
|
||||
</menu>
|
||||
</item>
|
||||
<item android:title="@string/toot_search">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/mastodon_search_portal"
|
||||
android:icon="?attr/ic_search"
|
||||
android:title="@string/mastodon_search_portal"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
|
||||
<item android:title="@string/setting">
|
||||
<menu>
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Toot search Powered by <a href="http://mastodonsearch.jp/">Mastodon Search Portal</a>, it indexes instances in Japan.<br/>If you want to remove your toot from search result, please contact to <a href="https://mstdn.jp/@mastodonsearch">@mastodonsearch@mstdn.jp</a>.
|
|
@ -0,0 +1 @@
|
|||
powered by <a href="http://mastodonsearch.jp/">マストドン検索ポータル</a><br/>検索結果からあなたのトゥートを除外したい場合、<a href="https://mstdn.jp/@mastodonsearch">@mastodonsearch@mstdn.jp</a>に連絡してください。
|
|
@ -366,8 +366,13 @@
|
|||
<string name="text_to_speech_shutdown">TextToSpeech shutdown…</string>
|
||||
<string name="show_post_button_bar_top">Show buttons bar at the top of posting screen</string>
|
||||
<string name="client_name">Client name (access token update required)</string>
|
||||
<string name="toot_search">Toot search</string>
|
||||
<string name="mastodon_search_portal">Mastodon Search Portal(JP)</string>
|
||||
<string name="search_desc_mastodon_api">Account/Hashtag search using Mastodon API.</string>
|
||||
<string name="toot_search_of">Toot Rechercher \"%1$s\"</string>
|
||||
<string name="progress_synchronize_toot">Syncing Toot…</string>
|
||||
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||
<!--<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
|
||||
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->
|
||||
|
|
|
@ -653,5 +653,10 @@
|
|||
<string name="text_to_speech_shutdown">TextToSpeechの後処理…</string>
|
||||
<string name="show_post_button_bar_top">投稿画面のボタンバーを上端に表示</string>
|
||||
<string name="client_name">クライアント名(アクセストークンの更新が必要)</string>
|
||||
<string name="toot_search">トゥート検索</string>
|
||||
<string name="mastodon_search_portal">マストドン検索ポータル(JP)</string>
|
||||
<string name="search_desc_mastodon_api">アカウントやハッシュタグをマストドンのAPIで検索します。</string>
|
||||
<string name="toot_search_of">トゥート検索:%1$s</string>
|
||||
<string name="progress_synchronize_toot">トゥートを同期してます…</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -361,4 +361,10 @@
|
|||
<string name="text_to_speech_shutdown">TextToSpeech shutdown…</string>
|
||||
<string name="show_post_button_bar_top">Show buttons bar at the top of posting screen</string>
|
||||
<string name="client_name">Client name (access token update required)</string>
|
||||
<string name="toot_search">Toot search</string>
|
||||
<string name="mastodon_search_portal">Mastodon Search Portal(JP)</string>
|
||||
<string name="search_desc_mastodon_api">Account/Hashtag search using Mastodon API.</string>
|
||||
<string name="toot_search_of">Toot search \"%1$s\"</string>
|
||||
<string name="progress_synchronize_toot">Syncing Toot…</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue