v1.4.3
- トゥート中のハッシュタグをタップした時に引用できるようにした
This commit is contained in:
parent
40e10d1b0f
commit
6178e99ea5
@ -9,8 +9,8 @@ android {
|
||||
applicationId "jp.juggler.subwaytooter"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 26
|
||||
versionCode 142
|
||||
versionName "1.4.2"
|
||||
versionCode 143
|
||||
versionName "1.4.3"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
@ -759,7 +759,7 @@ public class ActAccountSetting extends AppCompatActivity
|
||||
if( src.source != null && src.source.note != null ){
|
||||
etNote.setText( Emojione.decodeEmoji( this, src.source.note ) );
|
||||
}else if( src.note != null ){
|
||||
etNote.setText( HTMLDecoder.decodeHTML( ActAccountSetting.this, account, src.note, false, false, null ) );
|
||||
etNote.setText( HTMLDecoder.decodeHTML( ActAccountSetting.this, account, src.note, false, false, null ,null) );
|
||||
}else{
|
||||
etNote.setText( "" );
|
||||
}
|
||||
|
@ -1006,13 +1006,7 @@ public class ActAppSetting extends AppCompatActivity
|
||||
if( a.isPseudo() ) continue;
|
||||
list.add( a );
|
||||
}
|
||||
Collections.sort( list, new Comparator< SavedAccount >() {
|
||||
@Override
|
||||
public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( list );
|
||||
}
|
||||
|
||||
@Override public int getCount(){
|
||||
|
@ -22,6 +22,7 @@ import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.InputType;
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
@ -218,7 +219,7 @@ public class ActMain extends AppCompatActivity
|
||||
log.d( "onResume" );
|
||||
super.onResume();
|
||||
|
||||
MyClickableSpan.link_callback = link_click_listener;
|
||||
MyClickableSpan.link_callback = new WeakReference<>( link_click_listener );
|
||||
|
||||
if( pref.getBoolean( Pref.KEY_DONT_SCREEN_OFF, false ) ){
|
||||
getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
|
||||
@ -345,7 +346,6 @@ public class ActMain extends AppCompatActivity
|
||||
|
||||
app_state.stream_reader.onPause();
|
||||
|
||||
MyClickableSpan.link_callback = null;
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@ -1867,7 +1867,7 @@ public class ActMain extends AppCompatActivity
|
||||
task.executeOnExecutor( App1.task_executor );
|
||||
}
|
||||
|
||||
static final Pattern reHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)(?:\\z|\\?)" );
|
||||
static final Pattern reUrlHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)(?:\\z|\\?)" );
|
||||
static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)(?:\\z|\\?)" );
|
||||
static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)/(\\d+)(?:\\z|\\?)" );
|
||||
|
||||
@ -1879,7 +1879,7 @@ public class ActMain extends AppCompatActivity
|
||||
// トゥート検索カラムではaccess_infoは何にも紐ついていない
|
||||
|
||||
// ハッシュタグをアプリ内で開く
|
||||
Matcher m = reHashTag.matcher( url );
|
||||
Matcher m = reUrlHashTag.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 );
|
||||
@ -1918,7 +1918,7 @@ public class ActMain extends AppCompatActivity
|
||||
|
||||
if( ! noIntercept && access_info != null ){
|
||||
// ハッシュタグをアプリ内で開く
|
||||
Matcher m = reHashTag.matcher( url );
|
||||
Matcher m = reUrlHashTag.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 );
|
||||
@ -1997,14 +1997,42 @@ public class ActMain extends AppCompatActivity
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void openHashTag( int pos, SavedAccount access_info, String tag ){
|
||||
while( tag.startsWith( "#" ) ) tag = tag.substring( 1 );
|
||||
addColumn( pos, access_info, Column.TYPE_HASHTAG, tag );
|
||||
}
|
||||
|
||||
// 他インスタンスのハッシュタグの表示
|
||||
private void openHashTagOtherInstance( final int pos, final SavedAccount access_info, final String url, final String host, final String tag ){
|
||||
private void openHashTagOtherInstance( final int pos, final SavedAccount access_info, final String url, final String host, String tag ){
|
||||
while( tag.startsWith( "#" ) ) tag = tag.substring( 1 );
|
||||
openHashTagOtherInstance_sub(pos,access_info,url,host,tag);
|
||||
}
|
||||
|
||||
// 他インスタンスのハッシュタグの表示
|
||||
private void openHashTagOtherInstance_sub( final int pos, final SavedAccount access_info, final String url, final String host, final String tag ){
|
||||
|
||||
ActionsDialog dialog = new ActionsDialog();
|
||||
|
||||
// 各アカウント
|
||||
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( ActMain.this, log );
|
||||
|
||||
// ソートする
|
||||
SavedAccount.sort( account_list );
|
||||
|
||||
|
||||
ArrayList< SavedAccount > list_original = new ArrayList<>( );
|
||||
ArrayList< SavedAccount > list_original_pseudo = new ArrayList<>( );
|
||||
ArrayList< SavedAccount > list_other = new ArrayList<>( );
|
||||
for( SavedAccount a : account_list ){
|
||||
log.d("sort? %s",a.acct);
|
||||
if( ! host.equalsIgnoreCase( a.host ) ){
|
||||
list_other.add( a );
|
||||
}else if( a.isPseudo() ){
|
||||
list_original_pseudo.add( a );
|
||||
}else{
|
||||
list_original.add( a );
|
||||
}
|
||||
}
|
||||
|
||||
// ブラウザで表示する
|
||||
dialog.addAction( getString( R.string.open_web_on_host, host ), new Runnable() {
|
||||
@Override public void run(){
|
||||
@ -2012,33 +2040,8 @@ public class ActMain extends AppCompatActivity
|
||||
}
|
||||
} );
|
||||
|
||||
// 各アカウント
|
||||
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( ActMain.this, log );
|
||||
|
||||
// ソートする
|
||||
Collections.sort( account_list, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
|
||||
// 各アカウントで開く選択肢
|
||||
boolean has_host = false;
|
||||
for( SavedAccount a : account_list ){
|
||||
|
||||
if( host.equalsIgnoreCase( a.host ) ){
|
||||
has_host = true;
|
||||
}
|
||||
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( getString( R.string.open_in_account, a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openHashTag( pos, _a, tag );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
if( ! has_host ){
|
||||
if( list_original.isEmpty() && list_original_pseudo.isEmpty() ){
|
||||
// 疑似アカウントを作成して開く
|
||||
dialog.addAction( getString( R.string.open_in_pseudo_account, "?@" + host ), new Runnable() {
|
||||
@Override public void run(){
|
||||
SavedAccount sa = addPseudoAccount( host );
|
||||
@ -2049,12 +2052,43 @@ public class ActMain extends AppCompatActivity
|
||||
} );
|
||||
}
|
||||
|
||||
dialog.show( this, "#" + tag );
|
||||
//
|
||||
for( SavedAccount a : list_original ){
|
||||
final SavedAccount _a = a;
|
||||
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openHashTag( pos, _a, tag );
|
||||
}
|
||||
} );
|
||||
}
|
||||
//
|
||||
for( SavedAccount a : list_original_pseudo ){
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openHashTag( pos, _a, tag );
|
||||
}
|
||||
} );
|
||||
}
|
||||
//
|
||||
for( SavedAccount a : list_other ){
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openHashTag( pos, _a, tag );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
dialog.show( this,"#"+ tag );
|
||||
}
|
||||
|
||||
final MyClickableSpan.LinkClickCallback link_click_listener = new MyClickableSpan.LinkClickCallback() {
|
||||
@Override public void onClickLink( View view, LinkClickContext lcc, String url ){
|
||||
@Override public void onClickLink( View view, @NonNull final MyClickableSpan span){
|
||||
|
||||
View view_orig = view;
|
||||
|
||||
Column column = null;
|
||||
while( view != null ){
|
||||
Object tag = view.getTag();
|
||||
@ -2076,7 +2110,65 @@ public class ActMain extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
}
|
||||
openChromeTab( nextPosition( column ), (SavedAccount) lcc, url, false );
|
||||
final int pos = nextPosition( column );
|
||||
|
||||
// ハッシュタグはいきなり開くのではなくメニューがある
|
||||
Matcher m = reUrlHashTag.matcher( span.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
|
||||
final String host = m.group( 1 );
|
||||
final String tag = span.text.startsWith( "#" )? span.text : "#"+ Uri.decode( m.group( 2 ) );
|
||||
|
||||
ActionsDialog d = new ActionsDialog()
|
||||
.addAction( getString( R.string.open_hashtag_column ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openHashTagOtherInstance( pos, (SavedAccount) span.lcc, span.url, host, tag );
|
||||
}
|
||||
} )
|
||||
.addAction( getString( R.string.quote_hashtag_of ,tag ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openPost( tag + " ");
|
||||
}
|
||||
} );
|
||||
|
||||
final ArrayList<String> tag_list = new ArrayList<>( );
|
||||
try{
|
||||
//noinspection ConstantConditions
|
||||
CharSequence cs = ((TextView)view_orig).getText();
|
||||
if( cs instanceof Spannable){
|
||||
Spannable content = (Spannable) cs;
|
||||
for( MyClickableSpan s : content.getSpans( 0,content.length(), MyClickableSpan.class ) ){
|
||||
m = reUrlHashTag.matcher( s.url );
|
||||
if( m.find() ){
|
||||
String s_tag = s.text.startsWith( "#" )? s.text : "#"+Uri.decode( m.group( 2 ) );
|
||||
tag_list.add( s_tag );
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
log.trace(ex);
|
||||
}
|
||||
if(tag_list.size() > 1 ){
|
||||
StringBuilder sb = new StringBuilder( );
|
||||
for( String s : tag_list){
|
||||
if( sb.length() > 0 ) sb.append(' ');
|
||||
sb.append( s );
|
||||
}
|
||||
final String tag_all = sb.toString();
|
||||
d.addAction( getString( R.string.quote_all_hashtag_of ,tag_all), new Runnable() {
|
||||
@Override public void run(){
|
||||
openPost( tag_all +" ");
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
d.show(ActMain.this,tag);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
openChromeTab( pos, (SavedAccount) span.lcc, span.url, false );
|
||||
}
|
||||
};
|
||||
|
||||
@ -2800,14 +2892,10 @@ public class ActMain extends AppCompatActivity
|
||||
}
|
||||
|
||||
// ローカルアカウント
|
||||
Collections.sort( local_account_list, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( local_account_list );
|
||||
for( SavedAccount a : local_account_list ){
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( getString( R.string.open_in_account, AcctColor.getNickname( a.acct ) ), new Runnable() {
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openStatusLocal( pos, _a, status_id_original );
|
||||
}
|
||||
@ -2815,14 +2903,10 @@ public class ActMain extends AppCompatActivity
|
||||
}
|
||||
|
||||
// アクセスしたアカウント
|
||||
Collections.sort( access_account_list, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( access_account_list );
|
||||
for( SavedAccount a : access_account_list ){
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( getString( R.string.open_in_account, AcctColor.getNickname( a.acct ) ), new Runnable() {
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openStatusLocal( pos, _a, status_id_access );
|
||||
}
|
||||
@ -2830,14 +2914,10 @@ public class ActMain extends AppCompatActivity
|
||||
}
|
||||
|
||||
// その他の実アカウント
|
||||
Collections.sort( other_account_list, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( other_account_list);
|
||||
for( SavedAccount a : other_account_list ){
|
||||
final SavedAccount _a = a;
|
||||
dialog.addAction( getString( R.string.open_in_account, AcctColor.getNickname( a.acct ) ), new Runnable() {
|
||||
dialog.addAction( AcctColor.getStringWithNickname( ActMain.this,R.string.open_in_account,a.acct ), new Runnable() {
|
||||
@Override public void run(){
|
||||
openStatusRemote( pos, _a, url );
|
||||
}
|
||||
@ -4075,11 +4155,7 @@ public class ActMain extends AppCompatActivity
|
||||
dst.add( a );
|
||||
}
|
||||
}
|
||||
Collections.sort( dst, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( dst );
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -235,12 +236,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||
|
||||
@Override protected void onResume(){
|
||||
super.onResume();
|
||||
MyClickableSpan.link_callback = link_click_listener;
|
||||
MyClickableSpan.link_callback = new WeakReference<>( link_click_listener );
|
||||
}
|
||||
|
||||
@Override protected void onPause(){
|
||||
super.onPause();
|
||||
MyClickableSpan.link_callback = null;
|
||||
|
||||
// 編集中にホーム画面を押したり他アプリに移動する場合は下書きを保存する
|
||||
// やや過剰な気がするが、自アプリに戻ってくるときにランチャーからアイコンタップされると
|
||||
@ -619,13 +619,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||
ivReply = findViewById( R.id.ivReply );
|
||||
|
||||
account_list = SavedAccount.loadAccountList( ActPost.this, log );
|
||||
Collections.sort( account_list, new Comparator< SavedAccount >() {
|
||||
@Override
|
||||
public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( account_list);
|
||||
|
||||
btnAccount.setOnClickListener( this );
|
||||
btnVisibility.setOnClickListener( this );
|
||||
@ -1494,7 +1488,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||
llReply.setVisibility( View.GONE );
|
||||
}else{
|
||||
llReply.setVisibility( View.VISIBLE );
|
||||
tvReplyTo.setText( HTMLDecoder.decodeHTML( ActPost.this, account, in_reply_to_text, true, true, null ) );
|
||||
tvReplyTo.setText( HTMLDecoder.decodeHTML( ActPost.this, account, in_reply_to_text, true, true, null ,null) );
|
||||
ivReply.setImageUrl( pref, 16f, in_reply_to_image );
|
||||
}
|
||||
}
|
||||
@ -1899,7 +1893,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||
return null;
|
||||
}
|
||||
};
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( ActPost.this, lcc, text, false, false, null );
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( ActPost.this, lcc, text, false, false, null ,null);
|
||||
tvText.setText( sv );
|
||||
tvText.setMovementMethod( LinkMovementMethod.getInstance() );
|
||||
|
||||
@ -1921,11 +1915,10 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||
}
|
||||
|
||||
final MyClickableSpan.LinkClickCallback link_click_listener = new MyClickableSpan.LinkClickCallback() {
|
||||
@Override public void onClickLink( View view, LinkClickContext lcc, String url ){
|
||||
if( url == null ) return;
|
||||
@Override public void onClickLink( View view, @NonNull MyClickableSpan span ){
|
||||
// ブラウザで開く
|
||||
try{
|
||||
Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( url ) );
|
||||
Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( span.url ) );
|
||||
startActivity( intent );
|
||||
}catch( Throwable ex ){
|
||||
log.trace( ex );
|
||||
|
@ -71,7 +71,7 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
||||
addAfterLine( sb, "\n" );
|
||||
|
||||
intent.putExtra( EXTRA_CONTENT_START, sb.length() );
|
||||
sb.append( HTMLDecoder.decodeHTML( context,access_info, status.content, false, false, null ) );
|
||||
sb.append( HTMLDecoder.decodeHTML( context,access_info, status.content, false, false, null ,null) );
|
||||
intent.putExtra( EXTRA_CONTENT_END, sb.length() );
|
||||
|
||||
if( status instanceof TootStatus ){
|
||||
@ -121,7 +121,7 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
|
||||
|
||||
addAfterLine( sb, "\n" );
|
||||
|
||||
sb.append( HTMLDecoder.decodeHTML( context, access_info, ( who.note != null ? who.note : null ), false, false, null ) );
|
||||
sb.append( HTMLDecoder.decodeHTML( context, access_info, ( who.note != null ? who.note : null ), false, false, null ,null) );
|
||||
|
||||
addAfterLine( sb, "\n" );
|
||||
|
||||
|
@ -92,7 +92,7 @@ class HeaderViewHolderInstance extends HeaderViewHolderBase implements View.OnCl
|
||||
btnEmail.setText( supplyEmpty( instance.email ) );
|
||||
btnEmail.setEnabled( ! TextUtils.isEmpty( instance.email ) );
|
||||
|
||||
SpannableStringBuilder sb = HTMLDecoder.decodeHTML( activity, access_info, "<p>"+supplyEmpty( instance.description )+"</p>", false, true, null );
|
||||
SpannableStringBuilder sb = HTMLDecoder.decodeHTML( activity, access_info, "<p>"+supplyEmpty( instance.description )+"</p>", false, true, null ,null);
|
||||
int previous_br_count =0;
|
||||
for(int i=0;i<sb.length();++i){
|
||||
char c = sb.charAt( i );
|
||||
|
@ -37,7 +37,7 @@ class HeaderViewHolderSearchDesc extends HeaderViewHolderBase {
|
||||
}
|
||||
} );
|
||||
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( activity, access_info, html, false, true, null );
|
||||
CharSequence sv = HTMLDecoder.decodeHTML( activity, access_info, html, false, true, null ,null);
|
||||
|
||||
TextView tvSearchDesc = viewRoot.findViewById( R.id.tvSearchDesc );
|
||||
tvSearchDesc.setVisibility( View.VISIBLE );
|
||||
|
@ -134,6 +134,8 @@ public class TootApiClient {
|
||||
}
|
||||
|
||||
try{
|
||||
callback.publishApiProgress( context.getString( R.string.parsing_response ) );
|
||||
|
||||
//noinspection ConstantConditions
|
||||
String json = response.body().string();
|
||||
|
||||
|
@ -56,7 +56,15 @@ public class NicoEnquete {
|
||||
long time_start;
|
||||
long status_id;
|
||||
|
||||
public static NicoEnquete parse( @NonNull Context context, @NonNull SavedAccount access_info, @Nullable TootAttachment.List list_attachment, @Nullable String sv, long status_id, long time_start ){
|
||||
public static NicoEnquete parse(
|
||||
@NonNull Context context,
|
||||
@NonNull SavedAccount access_info,
|
||||
@Nullable TootAttachment.List list_attachment,
|
||||
@Nullable String sv,
|
||||
long status_id,
|
||||
long time_start ,
|
||||
@NonNull TootStatusLike status
|
||||
){
|
||||
try{
|
||||
if( sv != null ){
|
||||
JSONObject src = new JSONObject( sv );
|
||||
@ -69,7 +77,7 @@ public class NicoEnquete {
|
||||
dst.time_start = time_start;
|
||||
dst.status_id = status_id;
|
||||
if( dst.question != null ){
|
||||
dst.question = HTMLDecoder.decodeHTML( context, access_info, dst.question.toString(), true, true, list_attachment );
|
||||
dst.question = HTMLDecoder.decodeHTML( context, access_info, dst.question.toString(), true, true, list_attachment ,status);
|
||||
}
|
||||
if( dst.items != null ){
|
||||
for( int i = 0, ie = dst.items.size() ; i < ie ; ++ i ){
|
||||
|
@ -126,7 +126,7 @@ public class TootAccount {
|
||||
dst.statuses_count = src.optLong( "statuses_count" );
|
||||
|
||||
dst.note = Utils.optStringX( src, "note" );
|
||||
dst.decoded_note = HTMLDecoder.decodeHTML( context, account, ( dst.note != null ? dst.note : null ), true, true, null );
|
||||
dst.decoded_note = HTMLDecoder.decodeHTML( context, account, ( dst.note != null ? dst.note : null ), true, true, null ,null );
|
||||
|
||||
dst.url = Utils.optStringX( src, "url" );
|
||||
dst.avatar = Utils.optStringX( src, "avatar" ); // "https:\/\/mastodon.juggler.jp\/system\/accounts\/avatars\/000\/000\/148\/original\/0a468974fac5a448.PNG?1492081886",
|
||||
|
@ -142,11 +142,11 @@ public class TootStatus extends TootStatusLike {
|
||||
status.language = Utils.optStringX( src, "language" );
|
||||
|
||||
status.time_created_at = parseTime( status.created_at );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( context, access_info, status.content, true, true, status.media_attachments );
|
||||
status.decoded_content = HTMLDecoder.decodeHTML( context, access_info, status.content, true, true, status.media_attachments ,status);
|
||||
// status.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
status.decoded_mentions = HTMLDecoder.decodeMentions( access_info, status.mentions );
|
||||
status.decoded_mentions = HTMLDecoder.decodeMentions( access_info, status.mentions ,status);
|
||||
|
||||
status.enquete = NicoEnquete.parse( context,access_info , status.media_attachments , Utils.optStringX( src, "enquete"),status.id,status.time_created_at );
|
||||
status.enquete = NicoEnquete.parse( context,access_info , status.media_attachments , Utils.optStringX( src, "enquete"),status.id,status.time_created_at,status );
|
||||
|
||||
return status;
|
||||
}catch( Throwable ex ){
|
||||
|
@ -37,7 +37,7 @@ public class MSPAccount extends TootAccount {
|
||||
dst.id = src.optLong( "id" );
|
||||
|
||||
dst.note = Utils.optStringX( src, "note" );
|
||||
dst.decoded_note = HTMLDecoder.decodeHTML( context, access_info, ( dst.note != null ? dst.note : null ), true, true, null );
|
||||
dst.decoded_note = HTMLDecoder.decodeHTML( context, access_info, ( dst.note != null ? dst.note : null ), true, true, null ,null);
|
||||
|
||||
if( TextUtils.isEmpty( dst.url ) ){
|
||||
log.e( "parseAccount: missing url" );
|
||||
|
@ -80,7 +80,7 @@ public class MSPToot extends TootStatusLike {
|
||||
dst.setSpoilerText( context, Utils.optStringX( src, "spoiler_text" ) );
|
||||
|
||||
dst.content = Utils.optStringX( src, "content" );
|
||||
dst.decoded_content = HTMLDecoder.decodeHTML( context, access_info, dst.content, true, true, null );
|
||||
dst.decoded_content = HTMLDecoder.decodeHTML( context, access_info, dst.content, true, true, null ,dst);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
@ -100,11 +100,7 @@ public class AccountPicker {
|
||||
return;
|
||||
}
|
||||
|
||||
Collections.sort( account_list, new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
return String.CASE_INSENSITIVE_ORDER.compare( AcctColor.getNickname( a.acct ), AcctColor.getNickname( b.acct ) );
|
||||
}
|
||||
} );
|
||||
SavedAccount.sort( account_list );
|
||||
|
||||
@SuppressLint("InflateParams") View viewRoot = activity.getLayoutInflater().inflate( R.layout.dlg_account_picker, null, false );
|
||||
|
||||
|
@ -2,6 +2,8 @@ package jp.juggler.subwaytooter.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@ -11,22 +13,27 @@ import jp.juggler.subwaytooter.R;
|
||||
|
||||
public class ActionsDialog {
|
||||
|
||||
static class Action {
|
||||
String caption;
|
||||
Runnable r;
|
||||
private static class Action {
|
||||
@NonNull final CharSequence caption;
|
||||
@NonNull final Runnable r;
|
||||
|
||||
Action( @NonNull CharSequence caption, @NonNull Runnable r ){
|
||||
this.caption = caption;
|
||||
this.r = r;
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList< Action > action_list = new ArrayList<>();
|
||||
private final ArrayList< Action > action_list = new ArrayList<>();
|
||||
|
||||
public void addAction( String caption, Runnable r ){
|
||||
Action action = new Action();
|
||||
action.caption = caption;
|
||||
action.r = r;
|
||||
action_list.add( action );
|
||||
public ActionsDialog addAction( @NonNull CharSequence caption, @NonNull Runnable r ){
|
||||
|
||||
action_list.add( new Action( caption, r ) );
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void show( Context context, String title ){
|
||||
String[] caption_list = new String[ action_list.size() ];
|
||||
public ActionsDialog show( @NonNull Context context, @Nullable CharSequence title ){
|
||||
CharSequence[] caption_list = new CharSequence[ action_list.size() ];
|
||||
for( int i = 0, ie = caption_list.length ; i < ie ; ++ i ){
|
||||
caption_list[ i ] = action_list.get( i ).caption;
|
||||
}
|
||||
@ -44,5 +51,6 @@ public class ActionsDialog {
|
||||
|
||||
b.show();
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,24 @@
|
||||
package jp.juggler.subwaytooter.table;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import jp.juggler.subwaytooter.App1;
|
||||
import jp.juggler.subwaytooter.AppDataExporter;
|
||||
import jp.juggler.subwaytooter.Styler;
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -187,4 +193,27 @@ public class AcctColor {
|
||||
mMemoryCache.evictAll ();
|
||||
}
|
||||
|
||||
private static final char CHAR_REPLACE = 0x328A;
|
||||
|
||||
@NonNull public static CharSequence getStringWithNickname( @NonNull Context context, @NonNull int string_id , @NonNull String acct ){
|
||||
AcctColor ac = load( acct );
|
||||
if( ac == null ) return context.getString( string_id,acct );
|
||||
String name = ! TextUtils.isEmpty( ac.nickname ) ? Utils.sanitizeBDI( ac.nickname ) : acct ;
|
||||
int color_fg = hasColorForeground( ac ) ? ac.color_fg : Styler.getAttributeColor( context, android.R.attr.textColorPrimary );
|
||||
int color_bg = hasColorBackground( ac ) ? ac.color_bg : 0;
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder( context.getString( string_id,new String(new char[]{CHAR_REPLACE})) );
|
||||
for(int i=sb.length()-1;i>=0;--i){
|
||||
char c = sb.charAt( i );
|
||||
if( c != CHAR_REPLACE) continue;
|
||||
sb.replace( i,i+1,name );
|
||||
if( ac.color_fg != 0){
|
||||
sb.setSpan( new ForegroundColorSpan( ac.color_fg ), i, i + name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
if( ac.color_bg != 0){
|
||||
sb.setSpan( new BackgroundColorSpan( ac.color_bg ), i, i + name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -622,4 +624,25 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final Comparator< SavedAccount > account_comparator = new Comparator< SavedAccount >() {
|
||||
@Override public int compare( SavedAccount a, SavedAccount b ){
|
||||
int i;
|
||||
|
||||
// NA > !NA
|
||||
i = (a.isNA()? 1:0 ) - (b.isNA()? 1:0);
|
||||
if(i!=0) return i;
|
||||
|
||||
// pseudo > real
|
||||
i = (a.isPseudo()? 1:0 ) - (b.isPseudo()? 1:0);
|
||||
if(i!=0) return i;
|
||||
|
||||
String sa = AcctColor.getNickname( a.acct );
|
||||
String sb = AcctColor.getNickname( b.acct );
|
||||
return sa.compareToIgnoreCase( sb );
|
||||
}
|
||||
};
|
||||
|
||||
public static void sort( ArrayList< SavedAccount > account_list ){
|
||||
Collections.sort( account_list, account_comparator );
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +163,7 @@ public class HTMLDecoder {
|
||||
, boolean bShort
|
||||
, boolean bDecodeEmoji
|
||||
, @Nullable TootAttachment.List list_attachment
|
||||
, @Nullable Object link_tag
|
||||
){
|
||||
if( TAG_TEXT.equals( tag ) ){
|
||||
if( bDecodeEmoji ){
|
||||
@ -187,7 +188,7 @@ public class HTMLDecoder {
|
||||
sb_tmp.append( "<img/>" );
|
||||
}else{
|
||||
for( Node child : child_nodes ){
|
||||
child.encodeSpan( context, account, sb_tmp, bShort, bDecodeEmoji, list_attachment );
|
||||
child.encodeSpan( context, account, sb_tmp, bShort, bDecodeEmoji, list_attachment ,link_tag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +205,8 @@ public class HTMLDecoder {
|
||||
if( end > start && "a".equals( tag ) ){
|
||||
String href = getHref();
|
||||
if( href != null ){
|
||||
MyClickableSpan span = new MyClickableSpan( account, href, account.findAcctColor( href ) );
|
||||
String link_text = sb.subSequence( start,end ).toString();
|
||||
MyClickableSpan span = new MyClickableSpan( account, link_text, href, account.findAcctColor( href ),link_tag );
|
||||
sb.setSpan( span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
}
|
||||
@ -315,6 +317,7 @@ public class HTMLDecoder {
|
||||
, boolean bShort
|
||||
, boolean bDecodeEmoji
|
||||
, @Nullable TootAttachment.List list_attachment
|
||||
, @Nullable Object link_tag
|
||||
){
|
||||
prepareTagInformation();
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
@ -326,7 +329,7 @@ public class HTMLDecoder {
|
||||
rootNode.addChild( tracker, "" );
|
||||
}
|
||||
|
||||
rootNode.encodeSpan( context, account, sb, bShort, bDecodeEmoji, list_attachment );
|
||||
rootNode.encodeSpan( context, account, sb, bShort, bDecodeEmoji, list_attachment , link_tag );
|
||||
int end = sb.length();
|
||||
while( end > 0 && isWhitespace( sb.charAt( end - 1 ) ) ) -- end;
|
||||
if( end < sb.length() ){
|
||||
@ -366,7 +369,7 @@ public class HTMLDecoder {
|
||||
// return sb;
|
||||
// }
|
||||
|
||||
public static Spannable decodeMentions( final SavedAccount access_info, TootMention.List src_list ){
|
||||
public static Spannable decodeMentions( final SavedAccount access_info, TootMention.List src_list ,@Nullable Object link_tag ){
|
||||
if( src_list == null || src_list.isEmpty() ) return null;
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
for( TootMention item : src_list ){
|
||||
@ -380,7 +383,8 @@ public class HTMLDecoder {
|
||||
}
|
||||
int end = sb.length();
|
||||
if( end > start ){
|
||||
MyClickableSpan span = new MyClickableSpan( access_info, item.url, access_info.findAcctColor( item.url ) );
|
||||
String link_text = sb.subSequence( start,end ).toString();
|
||||
MyClickableSpan span = new MyClickableSpan( access_info, link_text,item.url, access_info.findAcctColor( item.url ) ,link_tag );
|
||||
|
||||
sb.setSpan( span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||
}
|
||||
|
@ -1,27 +1,42 @@
|
||||
package jp.juggler.subwaytooter.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import jp.juggler.subwaytooter.table.AcctColor;
|
||||
|
||||
public class MyClickableSpan extends ClickableSpan {
|
||||
|
||||
public interface LinkClickCallback {
|
||||
void onClickLink( View widget,LinkClickContext account, String url );
|
||||
void onClickLink( View widget,@NonNull MyClickableSpan span);
|
||||
}
|
||||
|
||||
public static LinkClickCallback link_callback;
|
||||
public static WeakReference<LinkClickCallback> link_callback = null;
|
||||
|
||||
public LinkClickContext account;
|
||||
public String url;
|
||||
private int color_fg;
|
||||
private int color_bg;
|
||||
public @NonNull LinkClickContext lcc;
|
||||
public @NonNull String text;
|
||||
public @NonNull String url;
|
||||
public @Nullable Object tag;
|
||||
public int color_fg;
|
||||
public int color_bg;
|
||||
|
||||
MyClickableSpan( LinkClickContext account, String url, AcctColor ac ){
|
||||
this.account = account;
|
||||
MyClickableSpan(
|
||||
@NonNull LinkClickContext lcc
|
||||
,@NonNull String text
|
||||
,@NonNull String url
|
||||
, AcctColor ac
|
||||
,@Nullable Object tag
|
||||
){
|
||||
this.lcc = lcc;
|
||||
this.text = text;
|
||||
this.url = url;
|
||||
this.tag = tag;
|
||||
|
||||
if( ac != null ){
|
||||
this.color_fg = ac.color_fg;
|
||||
this.color_bg = ac.color_bg;
|
||||
@ -29,8 +44,9 @@ public class MyClickableSpan extends ClickableSpan {
|
||||
}
|
||||
|
||||
@Override public void onClick( View widget ){
|
||||
if( link_callback != null ){
|
||||
link_callback.onClickLink( widget,this.account, url );
|
||||
LinkClickCallback cb = (link_callback == null ? null : link_callback.get() );
|
||||
if( cb != null ){
|
||||
cb.onClickLink( widget, this );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,8 +473,12 @@
|
||||
<string name="loading_notification_title">Loading notification…</string>
|
||||
<string name="error_notification_title">Server Timeout</string>
|
||||
<string name="error_notification_summary">If it\'s dead instance, please remove account on that server.</string>
|
||||
<string name="open_hashtag_column">Open hashtag column</string>
|
||||
<string name="quote_hashtag_of">Quote hashtag \"%1$s\"</string>
|
||||
<string name="quote_all_hashtag_of">Quote all hashtags \"%1$s\"</string>
|
||||
<string name="parsing_response">Parsing response…</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>-->
|
||||
|
@ -760,4 +760,8 @@
|
||||
<string name="loading_notification_title">通知の取得中…</string>
|
||||
<string name="error_notification_title">サーバータイムアウト</string>
|
||||
<string name="error_notification_summary">もし死んだインスタンスなら、そのサーバ上のアカウントを除去してください</string>
|
||||
<string name="open_hashtag_column">ハッシュタグのカラムを開く</string>
|
||||
<string name="quote_hashtag_of">ハッシュタグ \"%1$s\" を引用</string>
|
||||
<string name="quote_all_hashtag_of">全てのハッシュタグ \"%1$s\" を引用</string>
|
||||
<string name="parsing_response">応答の解析中…</string>
|
||||
</resources>
|
||||
|
@ -467,5 +467,9 @@
|
||||
<string name="loading_notification_title">Loading notification…</string>
|
||||
<string name="error_notification_title">Server Timeout</string>
|
||||
<string name="error_notification_summary">If it\'s dead instance, please remove account on that server.</string>
|
||||
<string name="open_hashtag_column">Open hashtag column</string>
|
||||
<string name="quote_hashtag_of">Quote hashtag \"%1$s\"</string>
|
||||
<string name="quote_all_hashtag_of">Quote all hashtags \"%1$s\"</string>
|
||||
<string name="parsing_response">Parsing response…</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user