From 276d4fd4c5b41eeeba3a4cdc5e17e95eeae30c9a Mon Sep 17 00:00:00 2001 From: tateisu Date: Thu, 21 Dec 2017 05:55:51 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dictionaries/tateisu.xml | 1 + app/build.gradle | 7 +- .../juggler/subwaytooter/ActColumnList.java | 5 +- .../java/jp/juggler/subwaytooter/ActMain.java | 71 +++++--------- .../jp/juggler/subwaytooter/ActMutedApp.java | 13 ++- .../jp/juggler/subwaytooter/ActMutedWord.java | 12 ++- .../java/jp/juggler/subwaytooter/Column.java | 16 ++- .../juggler/subwaytooter/ItemViewHolder.java | 3 +- .../juggler/subwaytooter/StatusButtons.java | 1 - .../subwaytooter/api/entity/TootAccount.java | 18 ++-- .../api/entity/TootStatusLike.java | 56 +++++++++++ .../api_msp/entity/MSPAccount.java | 5 +- .../{TootsearchClient.java => TsClient.java} | 48 ++++----- .../api_tootsearch/entity/TSAccount.java | 98 ------------------- .../api_tootsearch/entity/TSToot.java | 48 +++++++-- .../juggler/subwaytooter/table/AcctColor.java | 25 ++--- .../juggler/subwaytooter/table/MutedApp.java | 1 + .../juggler/subwaytooter/table/MutedWord.java | 1 + .../subwaytooter/table/SavedAccount.java | 64 ++++++------ .../subwaytooter/util/HTMLDecoder.java | 6 +- .../subwaytooter/util/LinkClickContext.java | 6 +- .../subwaytooter/util/MyClickableSpan.java | 2 +- 22 files changed, 237 insertions(+), 270 deletions(-) rename app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/{TootsearchClient.java => TsClient.java} (60%) delete mode 100644 app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSAccount.java diff --git a/.idea/dictionaries/tateisu.xml b/.idea/dictionaries/tateisu.xml index 6f09df78..9c9e64a6 100644 --- a/.idea/dictionaries/tateisu.xml +++ b/.idea/dictionaries/tateisu.xml @@ -16,6 +16,7 @@ firebase foregrounder gifv + github hashtag hashtags hohoemi diff --git a/app/build.gradle b/app/build.gradle index 16ab7f12..dd2b7b53 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,8 +57,6 @@ dependencies { exclude group: 'com.android.support', module: 'support-annotations' }) - // SDKのソースの27が提供されたタイミングで依存関係をまとめて上げる。 - // 現在特に問題を抱えていないし、それまでは依存関係のバージョンを上げない compile 'com.android.support:support-v4:26.1.0' compile 'com.android.support:appcompat-v7:26.1.0' @@ -69,7 +67,7 @@ dependencies { compile 'com.google.firebase:firebase-messaging:11.6.2' testCompile 'junit:junit:4.12' - compile 'commons-io:commons-io:2.6' + compile 'commons-io:commons-io:2.4' // 2.6にすると closeQuietly のかわりに try-with-resourceを使えと煩い compile 'uk.co.chrisjenx:calligraphy:2.3.0' compile 'com.github.woxthebox:draglistview:1.5.1' compile 'com.github.omadahealth:swipy:1.2.3@aar' @@ -78,6 +76,9 @@ dependencies { compile project(':exif') compile 'com.squareup.okhttp3:okhttp:3.9.1' + + // com.github.bumptech.glide 4.x はサポートライブラリ27に依存してる + // SDKのソースの27が提供されたら上げる compile 'com.github.bumptech.glide:glide:3.8.0' compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0' diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActColumnList.java b/app/src/main/java/jp/juggler/subwaytooter/ActColumnList.java index d6a7d68f..210f9637 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActColumnList.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActColumnList.java @@ -319,6 +319,7 @@ public class ActColumnList extends AppCompatActivity { private class MyListAdapter extends DragItemAdapter< MyItem, MyViewHolder > { + MyListAdapter(){ super(); setHasStableIds( true ); @@ -337,10 +338,10 @@ public class ActColumnList extends AppCompatActivity { holder.bind( getItemList().get( position ) ); } - @Override - public long getItemId( int position ){ + @Override public long getUniqueItemId( int position ){ MyItem item = mItemList.get( position ); // mItemList は親クラスのメンバ変数 return item.id; } + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java index e1125792..a9dd1c9a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java @@ -7,7 +7,6 @@ import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Typeface; @@ -16,7 +15,6 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.customtabs.CustomTabsIntent; @@ -1304,25 +1302,25 @@ public class ActMain extends AppCompatActivity // Android 5.xまでは MATCH_DEFAULT_ONLY でマッチするすべてのアプリを取得できる query_flag = PackageManager.MATCH_DEFAULT_ONLY; } - + // queryIntentActivities に渡すURLは実在しないホストのものにする - Intent intent = new Intent( Intent.ACTION_VIEW ,Uri.parse( "https://dummy.subwaytooter.club/" )); + Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( "https://dummy.subwaytooter.club/" ) ); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); List< ResolveInfo > resolveInfoList = getPackageManager().queryIntentActivities( intent, query_flag ); if( resolveInfoList.isEmpty() ){ throw new RuntimeException( "resolveInfoList is empty." ); } - + // このアプリ以外の選択肢を集める String my_name = getPackageName(); ArrayList< Intent > choice_list = new ArrayList<>(); for( ResolveInfo ri : resolveInfoList ){ - + // 選択肢からこのアプリを除外 if( my_name.equals( ri.activityInfo.packageName ) ) continue; - + // 選択肢のIntentは目的のUriで作成する - Intent choice = new Intent( Intent.ACTION_VIEW ,uri); + Intent choice = new Intent( Intent.ACTION_VIEW, uri ); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); choice.setPackage( ri.activityInfo.packageName ); choice.setClassName( ri.activityInfo.packageName, ri.activityInfo.name ); @@ -1774,8 +1772,8 @@ public class ActMain extends AppCompatActivity } 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|[?#])" ); + static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([A-Za-z0-9_]+)(?:\\z|[?#])" ); + static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([A-Za-z0-9_]+)/(\\d+)(?:\\z|[?#])" ); public void openChromeTab( final int pos, @Nullable final SavedAccount access_info, final String url, boolean noIntercept ){ try{ @@ -2589,15 +2587,6 @@ public class ActMain extends AppCompatActivity } } - // OStatus - static final Pattern reTootUriOS = Pattern.compile( "tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE ); - // ActivityPub 1 - static final Pattern reTootUriAP1 = Pattern.compile( "https?://([^/]+)/users/[^/]+/statuses/(\\d+)" ); - // ActivityPub 2 - static final Pattern reTootUriAP2 = Pattern.compile( "https?://([^/]+)/@[^/]+/(\\d+)" ); - - // static final Pattern reUriActivityPubToot = Pattern.compile( "tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE ); - public void openStatusOtherInstance( int pos, @NonNull SavedAccount access_info, @Nullable TootStatusLike status ){ // アカウント情報がないと出来ないことがある if( status == null || status.account == null ) return; @@ -2609,13 +2598,16 @@ public class ActMain extends AppCompatActivity ); }else if( status instanceof TSToot ){ // Tootsearch ではステータスのアクセス元ホストは分からない - // ステータスの投稿元ホストでのIDも分からない + + // uri から投稿元タンスでのステータスIDを調べる + long status_id_original = TootStatusLike.parseStatusId( status ); + openStatusOtherInstance( pos, access_info, status.url - , - 1L + , status_id_original , null, - 1L ); + }else if( status instanceof TootStatus ){ - TootStatus ts = (TootStatus) status; if( status.host_original.equals( status.host_access ) ){ // TLアカウントのホストとトゥートのアカウントのホストが同じ場合 openStatusOtherInstance( pos, access_info, status.url @@ -2624,28 +2616,8 @@ public class ActMain extends AppCompatActivity ); }else{ // TLアカウントのホストとトゥートのアカウントのホストが異なる場合 - - long status_id_original = - 1L; - - try{ - // UriにステータスIDが含まれている場合がある - Matcher m = reTootUriOS.matcher( ts.uri ); - if( m.find() ){ - status_id_original = Long.parseLong( m.group( 2 ), 10 ); - }else{ - m = reTootUriAP1.matcher( ts.uri ); - if( m.find() ){ - status_id_original = Long.parseLong( m.group( 2 ), 10 ); - }else{ - m = reTootUriAP2.matcher( ts.uri ); - if( m.find() ){ - status_id_original = Long.parseLong( m.group( 2 ), 10 ); - } - } - } - }catch( Throwable ex ){ - log.e( ex, "openStatusOtherInstance: cant parse tag: %s", ts.uri ); - } + // uri から投稿元タンスでのステータスIDを調べる + long status_id_original = TootStatusLike.parseStatusId( status ); openStatusOtherInstance( pos, access_info, status.url , status_id_original @@ -2673,12 +2645,20 @@ public class ActMain extends AppCompatActivity } } ); + // トゥートの投稿元タンスにあるアカウント ArrayList< SavedAccount > local_account_list = new ArrayList<>(); + + // TLを読んだタンスにあるアカウント ArrayList< SavedAccount > access_account_list = new ArrayList<>(); + + // その他のタンスにあるアカウント ArrayList< SavedAccount > other_account_list = new ArrayList<>(); + for( SavedAccount a : SavedAccount.loadAccountList( ActMain.this, log ) ){ + // 疑似アカウントは後でまとめて処理する if( a.isPseudo() ) continue; + if( status_id_original >= 0L && host_original.equalsIgnoreCase( a.host ) ){ // アクセス情報+ステータスID でアクセスできるなら // 同タンスのアカウントならステータスIDの変換なしに表示できる @@ -2800,8 +2780,7 @@ public class ActMain extends AppCompatActivity return result; } - @Override - protected void handleResult( TootApiResult result ){ + @Override protected void handleResult( TootApiResult result ){ if( result == null ){ // cancelled. diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMutedApp.java b/app/src/main/java/jp/juggler/subwaytooter/ActMutedApp.java index d83d8de4..94e39fce 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMutedApp.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMutedApp.java @@ -111,10 +111,12 @@ public class ActMutedApp extends AppCompatActivity { Cursor cursor = MutedApp.createCursor(); if( cursor != null ){ try{ + int idx_id = cursor.getColumnIndex( MutedApp.COL_ID ); int idx_name = cursor.getColumnIndex( MutedApp.COL_NAME ); while( cursor.moveToNext() ){ + long id = cursor.getLong( idx_id ); String name = cursor.getString( idx_name ); - MyItem item = new MyItem( name ); + MyItem item = new MyItem( id, name ); tmp_list.add( item ); } @@ -130,9 +132,11 @@ public class ActMutedApp extends AppCompatActivity { // リスト要素のデータ static class MyItem { + final long id; final String name; - MyItem( String name ){ + MyItem( long id,String name ){ + this.id = id; this.name = name; } } @@ -212,10 +216,9 @@ public class ActMutedApp extends AppCompatActivity { holder.bind( getItemList().get( position ) ); } - @Override - public long getItemId( int position ){ + @Override public long getUniqueItemId( int position ){ MyItem item = mItemList.get( position ); // mItemList は親クラスのメンバ変数 - return item.name.hashCode(); + return item.id; } } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMutedWord.java b/app/src/main/java/jp/juggler/subwaytooter/ActMutedWord.java index cbb4443b..a49841a0 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMutedWord.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMutedWord.java @@ -111,10 +111,12 @@ public class ActMutedWord extends AppCompatActivity { Cursor cursor = MutedWord.createCursor(); if( cursor != null ){ try{ + int idx_id = cursor.getColumnIndex( MutedWord.COL_ID ); int idx_name = cursor.getColumnIndex( MutedWord.COL_NAME ); while( cursor.moveToNext() ){ + long id = cursor.getLong( idx_id ); String name = cursor.getString( idx_name ); - MyItem item = new MyItem( name ); + MyItem item = new MyItem( id, name ); tmp_list.add( item ); } @@ -130,9 +132,11 @@ public class ActMutedWord extends AppCompatActivity { // リスト要素のデータ static class MyItem { + final long id; final String name; - MyItem( String name ){ + MyItem( long id,String name ){ + this.id = id; this.name = name; } } @@ -213,9 +217,9 @@ public class ActMutedWord extends AppCompatActivity { } @Override - public long getItemId( int position ){ + public long getUniqueItemId( int position ){ MyItem item = mItemList.get( position ); // mItemList は親クラスのメンバ変数 - return item.name.hashCode(); + return item.id; } } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java index 2d303fa0..8591c81c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java @@ -42,7 +42,7 @@ import jp.juggler.subwaytooter.api.entity.TootStatus; import jp.juggler.subwaytooter.api.entity.TootTag; import jp.juggler.subwaytooter.api_msp.MSPClient; import jp.juggler.subwaytooter.api_msp.entity.MSPToot; -import jp.juggler.subwaytooter.api_tootsearch.TootsearchClient; +import jp.juggler.subwaytooter.api_tootsearch.TSClient; import jp.juggler.subwaytooter.api_tootsearch.entity.TSToot; import jp.juggler.subwaytooter.table.AcctColor; import jp.juggler.subwaytooter.table.AcctSet; @@ -1851,7 +1851,7 @@ import jp.juggler.subwaytooter.util.Utils; list_tmp = new ArrayList<>(); result = new TootApiResult(); }else{ - result = TootsearchClient.search( context, search_query, max_id, new TootsearchClient.Callback() { + result = TSClient.search( context, search_query, max_id, new TSClient.Callback() { @Override public boolean isApiCancelled(){ return isCancelled() || is_dispose.get(); } @@ -1869,7 +1869,7 @@ import jp.juggler.subwaytooter.util.Utils; if( result != null ){ if( result.object != null ){ // max_id の更新 - max_id = TootsearchClient.getMaxId( result.object, max_id ); + max_id = TSClient.getMaxId( result.object, max_id ); // リストデータの用意 TSToot.List search_result = TSToot.parseList( context, access_info, result.object ); list_tmp = new ArrayList<>(); @@ -2801,7 +2801,7 @@ import jp.juggler.subwaytooter.util.Utils; list_tmp = new ArrayList<>(); result = new TootApiResult( context.getString( R.string.end_of_list ) ); }else{ - result = TootsearchClient.search( context, search_query, max_id, new TootsearchClient.Callback() { + result = TSClient.search( context, search_query, max_id, new TSClient.Callback() { @Override public boolean isApiCancelled(){ return isCancelled() || is_dispose.get(); } @@ -2818,13 +2818,11 @@ import jp.juggler.subwaytooter.util.Utils; } ); if( result != null && result.object != null ){ // max_id の更新 - max_id = TootsearchClient.getMaxId( result.object, max_id ); + max_id = TSClient.getMaxId( result.object, max_id ); // リストデータの用意 TSToot.List search_result = TSToot.parseList( context, access_info, result.object ); - if( search_result != null ){ - list_tmp = new ArrayList<>(); - addWithFilter( list_tmp, search_result ); - } + list_tmp = new ArrayList<>(); + addWithFilter( list_tmp, search_result ); } } return result; diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java index 77b78e9d..594b58e5 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java @@ -828,7 +828,8 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { case R.id.btnSearchTag: if( search_tag != null ){ - activity.openHashTag( activity.nextPosition( column ), access_info, search_tag.substring( 1 ) ); + // search_tag は#を含まない + activity.openHashTag( activity.nextPosition( column ), access_info, search_tag ); }else if( gap != null ){ column.startGap( gap ); }else if( domain_block != null ){ diff --git a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java index b9af34f7..c48c3a53 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java +++ b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java @@ -13,7 +13,6 @@ import android.widget.PopupWindow; 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.SavedAccount; import jp.juggler.subwaytooter.table.UserRelation; import jp.juggler.subwaytooter.util.LogCategory; diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.java b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.java index 55167442..11cf74a3 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.java @@ -21,6 +21,8 @@ import jp.juggler.subwaytooter.util.Utils; public class TootAccount { private static final LogCategory log = new LogCategory( "TootAccount" ); + public static final Pattern reAccountUrl = Pattern.compile( "\\Ahttps://([A-Za-z0-9.-]+)/@([A-Za-z0-9_]+)(?:\\z|[?#])" ); + public static class List extends ArrayList< TootAccount > { } @@ -28,7 +30,7 @@ public class TootAccount { // The ID of the account public long id; - // The username of the account + // The username of the account /[A-Za-z0-9_]{1,30}/ public String username; // Equals username for local users, includes @domain for remote ones @@ -108,13 +110,12 @@ public class TootAccount { this.username = username; } - public static TootAccount parse( Context context, LinkClickContext account, JSONObject src ){ + public static TootAccount parse( @NonNull Context context, @NonNull LinkClickContext account, @Nullable JSONObject src ){ return parse( context, account, src, new TootAccount() ); } - @Nullable - public static TootAccount parse( Context context, LinkClickContext account, JSONObject src, TootAccount dst ){ + public static TootAccount parse( @NonNull Context context, @NonNull LinkClickContext account, @Nullable JSONObject src, @NonNull TootAccount dst ){ if( src == null ) return null; try{ dst.id = Utils.optLongX( src, "id", - 1L ); @@ -155,7 +156,7 @@ public class TootAccount { JSONObject o = src.optJSONObject( "moved" ); if( o != null ){ - dst.moved = TootAccount.parse( context, account, o); + dst.moved = TootAccount.parse( context, account, o ); } return dst; @@ -178,8 +179,6 @@ public class TootAccount { return dst; } - - @NonNull public static List parseList( Context context, LinkClickContext account, JSONArray array ){ List result = new List(); @@ -197,10 +196,9 @@ public class TootAccount { } private static final Pattern reWhitespace = Pattern.compile( "[\\s\\t\\x0d\\x0a]+" ); - public Spannable decodeDisplayName( Context context ){ - + // remove white spaces String sv = reWhitespace.matcher( display_name ).replaceAll( " " ); @@ -214,7 +212,7 @@ public class TootAccount { }else{ this.display_name = Utils.sanitizeBDI( sv ); } - this.decoded_display_name = decodeDisplayName(context); + this.decoded_display_name = decodeDisplayName( context ); } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatusLike.java b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatusLike.java index 6919a2a7..9bfe35fb 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatusLike.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatusLike.java @@ -1,6 +1,7 @@ package jp.juggler.subwaytooter.api.entity; import android.content.Context; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.Spannable; import android.text.TextUtils; @@ -8,14 +9,19 @@ import android.text.TextUtils; import org.json.JSONObject; import java.lang.ref.WeakReference; +import java.util.regex.Matcher; import java.util.regex.Pattern; +import jp.juggler.subwaytooter.api_tootsearch.entity.TSToot; import jp.juggler.subwaytooter.table.SavedAccount; import jp.juggler.subwaytooter.util.DecodeOptions; +import jp.juggler.subwaytooter.util.LogCategory; import jp.juggler.subwaytooter.util.Utils; public abstract class TootStatusLike extends TootId { + static final LogCategory log = new LogCategory("TootStatusLike"); + //URL to the status page (can be remote) public String url; @@ -115,4 +121,54 @@ public abstract class TootStatusLike extends TootId { public int originalLineCount; } public AutoCW auto_cw; + + + // OStatus + static final Pattern reTootUriOS = Pattern.compile( "tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE ); + // ActivityPub 1 + static final Pattern reTootUriAP1 = Pattern.compile( "https?://([^/]+)/users/[A-Za-z0-9_]+/statuses/(\\d+)" ); + // ActivityPub 2 + static final Pattern reTootUriAP2 = Pattern.compile( "https?://([^/]+)/@[A-Za-z0-9_]+/(\\d+)" ); + + public static long parseStatusId( @NonNull TootStatusLike status ){ + + String uri; + if( status instanceof TootStatus ){ + uri = ( (TootStatus) status ).uri; + }else if( status instanceof TSToot ){ + uri = ( (TSToot) status ).uri; + }else{ + log.d( "parseStatusId: unsupported status type: %s", status.getClass().getSimpleName() ); + return - 1L; + } + + try{ + Matcher m; + + // https://friends.nico/users/(who)/statuses/(status_id) + m = reTootUriAP1.matcher( uri ); + if( m.find() ){ + return Long.parseLong( m.group( 2 ), 10 ); + } + + // tag:mstdn.osaka,2017-12-19:objectId=5672321:objectType=Status + m = reTootUriOS.matcher( uri ); + if( m.find() ){ + return Long.parseLong( m.group( 2 ), 10 ); + } + + // + m = reTootUriAP2.matcher( uri ); + if( m.find() ){ + return Long.parseLong( m.group( 2 ), 10 ); + } + + log.d( "parseStatusId: unsupported status uri: %s", uri ); + + }catch( Throwable ex ){ + log.e( ex, "parseStatusId: cant parse tag: %s", uri ); + } + + return - 1L; + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api_msp/entity/MSPAccount.java b/app/src/main/java/jp/juggler/subwaytooter/api_msp/entity/MSPAccount.java index 0b451cba..da3a9ea6 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api_msp/entity/MSPAccount.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api_msp/entity/MSPAccount.java @@ -8,7 +8,6 @@ import android.text.TextUtils; import org.json.JSONObject; import java.util.regex.Matcher; -import java.util.regex.Pattern; import jp.juggler.subwaytooter.api.entity.TootAccount; import jp.juggler.subwaytooter.table.SavedAccount; @@ -19,10 +18,8 @@ import jp.juggler.subwaytooter.util.Utils; public class MSPAccount extends TootAccount { private static final LogCategory log = new LogCategory( "MSPAccount" ); - private static final Pattern reAccountUrl = Pattern.compile( "\\Ahttps://([^/#?]+)/@([^/#?]+)\\z" ); - @Nullable - static TootAccount parseAccount( @NonNull Context context, SavedAccount access_info, JSONObject src ){ + static TootAccount parseAccount( @NonNull Context context, @NonNull SavedAccount access_info, @Nullable JSONObject src ){ if( src == null ) return null; diff --git a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TootsearchClient.java b/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TsClient.java similarity index 60% rename from app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TootsearchClient.java rename to app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TsClient.java index 510f9fe6..16622047 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TootsearchClient.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/TsClient.java @@ -1,19 +1,15 @@ package jp.juggler.subwaytooter.api_tootsearch; 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.api.TootApiResult; -import jp.juggler.subwaytooter.api_msp.MSPApiResult; import jp.juggler.subwaytooter.util.LogCategory; import jp.juggler.subwaytooter.util.Utils; import okhttp3.Call; @@ -21,60 +17,54 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -public class TootsearchClient { - private 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"; +public class TSClient { + private static final LogCategory log = new LogCategory( "TSClient" ); private static final OkHttpClient ok_http_client = App1.ok_http_client; public interface Callback { boolean isApiCancelled(); + void publishApiProgress( String s ); } public static TootApiResult search( @NonNull Context context - ,@NonNull String query - ,@NonNull String max_id // 空文字列、もしくはfromに指定するパラメータ + , @NonNull String query + , @NonNull String max_id // 空文字列、もしくはfromに指定するパラメータ , @NonNull Callback callback ){ - SharedPreferences pref = Pref.pref( context ); + String url = "https://tootsearch.chotto.moe/api/v1/search" + + "?sort=" + Uri.encode( "created_at:desc" ) + + "&from=" + max_id + + "&q=" + Uri.encode( query ); + Response response; - - callback.publishApiProgress( "waiting search result..." ); - - StringBuilder sb = new StringBuilder(); - sb.append("https://tootsearch.chotto.moe/api/v1/search?sort=created_at%3Adesc"); - sb.append( "&from=").append(max_id ); - sb.append( "&q=").append( Uri.encode( query ) ); - String url = sb.toString(); - try{ Request request = new Request.Builder() .url( url ) .build(); - + + callback.publishApiProgress( "waiting search result..." ); Call call = ok_http_client.newCall( request ); response = call.execute(); }catch( Throwable ex ){ log.trace( ex ); return new TootApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) ); } - + if( callback.isApiCancelled() ) return null; - + if( ! response.isSuccessful() ){ - log.d("response failed."); + log.d( "response failed." ); return new TootApiResult( Utils.formatResponse( response, url ) ); } + try{ //noinspection ConstantConditions String json = response.body().string(); JSONObject object = new JSONObject( json ); - return new TootApiResult( response,null,json,object ); + return new TootApiResult( response, null, json, object ); }catch( Throwable ex ){ log.trace( ex ); return new TootApiResult( Utils.formatError( ex, "API data error" ) ); @@ -91,9 +81,9 @@ public class TootsearchClient { // returns the number for "from" parameter of next page. // returns "" if no more next page. - public static String getMaxId( @NonNull JSONObject root,String old ){ + public static String getMaxId( @NonNull JSONObject root, String old ){ int old_from = Utils.parse_int( old, 0 ); - JSONArray hits2 = getHits( root); + JSONArray hits2 = getHits( root ); if( hits2 != null ){ int size = hits2.length(); return size == 0 ? "" : Integer.toString( old_from + hits2.length() ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSAccount.java b/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSAccount.java deleted file mode 100644 index 1c240115..00000000 --- a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSAccount.java +++ /dev/null @@ -1,98 +0,0 @@ -package jp.juggler.subwaytooter.api_tootsearch.entity; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import org.json.JSONObject; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji; -import jp.juggler.subwaytooter.api.entity.TootAccount; -import jp.juggler.subwaytooter.api.entity.TootStatus; -import jp.juggler.subwaytooter.table.SavedAccount; -import jp.juggler.subwaytooter.util.DecodeOptions; -import jp.juggler.subwaytooter.util.LogCategory; -import jp.juggler.subwaytooter.util.Utils; - -public class TSAccount extends TootAccount { - private static final LogCategory log = new LogCategory( "TSAccount" ); - - private static final Pattern reAccountUrl = Pattern.compile( "\\Ahttps://([^/#?]+)/@([^/#?]+)\\z" ); - - @Nullable - static TootAccount parseAccount( @NonNull Context context, SavedAccount access_info, JSONObject src ){ - - if( src == null ) return null; - - TSAccount dst = new TSAccount(); - - dst.url = Utils.optStringX( src, "url" ); - if( TextUtils.isEmpty( dst.url ) ){ - log.e( "parseAccount: missing url" ); - return null; - } - - // tootsearch のアカウントのIDはどのタンス上のものか分からない - ////// dst.id = Utils.optLongX( src, "id" ); - dst.id = -1L; - dst.username = Utils.optStringX( src, "username" ); - - dst.id = Utils.optLongX( src, "id", - 1L ); - dst.username = Utils.optStringX( src, "username" ); - - dst.acct = Utils.optStringX( src, "acct" ); - if( dst.acct == null ){ - dst.acct = "?@?"; - }else if( -1 == dst.acct.indexOf( '@' ) ){ - 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 ); - } - } - - // 絵文字データは先に読んでおく - dst.profile_emojis = NicoProfileEmoji.parseMap( src.optJSONArray( "profile_emojis" ) ); - - String sv = Utils.optStringX( src, "display_name" ); - dst.setDisplayName( context, dst.username, sv ); - - dst.locked = src.optBoolean( "locked" ); - dst.created_at = Utils.optStringX( src, "created_at" ); - dst.followers_count = Utils.optLongX( src, "followers_count" ); - dst.following_count = Utils.optLongX( src, "following_count" ); - dst.statuses_count = Utils.optLongX( src, "statuses_count" ); - - dst.note = Utils.optStringX( src, "note" ); - dst.decoded_note = new DecodeOptions() - .setShort( true ) - .setDecodeEmoji( true ) - .setProfileEmojis( dst.profile_emojis ) - .decodeHTML( context, access_info, dst.note ); - - dst.avatar = Utils.optStringX( src, "avatar" ); // "https:\/\/mastodon.juggler.jp\/system\/accounts\/avatars\/000\/000\/148\/original\/0a468974fac5a448.PNG?1492081886", - dst.avatar_static = Utils.optStringX( src, "avatar_static" ); // "https:\/\/mastodon.juggler.jp\/system\/accounts\/avatars\/000\/000\/148\/original\/0a468974fac5a448.PNG?1492081886", - dst.header = Utils.optStringX( src, "header" ); // "https:\/\/mastodon.juggler.jp\/headers\/original\/missing.png" - dst.header_static = Utils.optStringX( src, "header_static" ); // "https:\/\/mastodon.juggler.jp\/headers\/original\/missing.png"} - - // ,"nico_url":null - - dst.time_created_at = TootStatus.parseTime( dst.created_at ); - - dst.source = parseSource( src.optJSONObject( "source" ) ); - -// JSONObject o = src.optJSONObject( "moved" ); -// if( o != null ){ -// dst.moved = TootAccount.parse( context, account, o); -// } - - return dst; - - } -} diff --git a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSToot.java b/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSToot.java index facfd630..23e3e9bd 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSToot.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api_tootsearch/entity/TSToot.java @@ -10,13 +10,15 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.HashSet; +import java.util.regex.Matcher; import jp.juggler.subwaytooter.api.entity.CustomEmoji; import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji; +import jp.juggler.subwaytooter.api.entity.TootAccount; import jp.juggler.subwaytooter.api.entity.TootAttachment; import jp.juggler.subwaytooter.api.entity.TootStatus; import jp.juggler.subwaytooter.api.entity.TootStatusLike; -import jp.juggler.subwaytooter.api_tootsearch.TootsearchClient; +import jp.juggler.subwaytooter.api_tootsearch.TSClient; import jp.juggler.subwaytooter.table.SavedAccount; import jp.juggler.subwaytooter.util.DecodeOptions; import jp.juggler.subwaytooter.util.LogCategory; @@ -39,7 +41,7 @@ public class TSToot extends TootStatusLike { if( src == null ) return null; TSToot dst = new TSToot(); - dst.account = TSAccount.parseAccount( context, access_info, src.optJSONObject( "account" ) ); + dst.account = parseAccount( context, access_info, src.optJSONObject( "account" ) ); if( dst.account == null ){ log.e( "missing status account" ); return null; @@ -47,16 +49,15 @@ public class TSToot extends TootStatusLike { dst.json = src; - // 絵文字マップは割と最初の方で読み込んでおきたい - dst.custom_emojis = CustomEmoji.parseMap( src.optJSONArray( "emojis" ),access_info.host); + dst.custom_emojis = CustomEmoji.parseMap( src.optJSONArray( "emojis" ), access_info.host ); dst.profile_emojis = NicoProfileEmoji.parseMap( src.optJSONArray( "profile_emojis" ) ); dst.url = Utils.optStringX( src, "url" ); dst.uri = Utils.optStringX( src, "uri" ); dst.host_original = dst.account.getAcctHost(); dst.host_access = "?"; - dst.id = -1L; // Utils.optLongX( src, "id", - 1L ); + dst.id = - 1L; // Utils.optLongX( src, "id", - 1L ); if( TextUtils.isEmpty( dst.url ) || TextUtils.isEmpty( dst.host_original ) ){ log.e( "missing status url or host or id" ); @@ -68,7 +69,7 @@ public class TSToot extends TootStatusLike { dst.media_attachments = TootAttachment.parseList( src.optJSONArray( "media_attachments" ) ); - dst.sensitive = src.optBoolean( "sensitive" ,false ); + dst.sensitive = src.optBoolean( "sensitive", false ); dst.setSpoilerText( context, Utils.optStringX( src, "spoiler_text" ) ); @@ -87,9 +88,10 @@ public class TSToot extends TootStatusLike { public static class List extends ArrayList< TSToot > { } - @NonNull public static TSToot.List parseList( @NonNull Context context, SavedAccount access_info, @NonNull JSONObject root ){ + @NonNull + public static TSToot.List parseList( @NonNull Context context, SavedAccount access_info, @NonNull JSONObject root ){ TSToot.List list = new TSToot.List(); - JSONArray array = TootsearchClient.getHits( root ); + JSONArray array = TSClient.getHits( root ); if( array != null ){ for( int i = 0, ie = array.length() ; i < ie ; ++ i ){ JSONObject src = array.optJSONObject( i ); @@ -128,4 +130,34 @@ public class TSToot extends TootStatusLike { @Override public boolean canPin( SavedAccount access_info ){ return false; } + + @Nullable + private static TootAccount parseAccount( @NonNull Context context, @NonNull SavedAccount access_info, @Nullable JSONObject src ){ + + TootAccount dst = TootAccount.parse( context, access_info, src ); + if( dst != null ){ + + // tootsearch のアカウントのIDはどのタンス上のものか分からない + dst.id = - 1L; + + // この後の処理でURLを使うので、URLがないならパースエラーとする + if( TextUtils.isEmpty( dst.url ) ){ + log.e( "parseAccount: missing url" ); + return null; + } + + if( - 1 == dst.acct.indexOf( '@' ) ){ + Matcher m = TootAccount.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; + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/AcctColor.java b/app/src/main/java/jp/juggler/subwaytooter/table/AcctColor.java index 840c0d57..a7b6dd46 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/AcctColor.java +++ b/app/src/main/java/jp/juggler/subwaytooter/table/AcctColor.java @@ -58,7 +58,7 @@ public class AcctColor { onDBCreate( db ); return; } - + if( oldVersion < 17 && newVersion >= 17 ){ try{ db.execSQL( "alter table " + table + " add column " + COL_NOTIFICATION_SOUND + " text default ''" ); @@ -72,9 +72,9 @@ public class AcctColor { public int color_fg; public int color_bg; public String nickname; - public String notification_sound ; + public String notification_sound; - public AcctColor( @NonNull String acct, String nickname, int color_fg, int color_bg ,String notification_sound){ + public AcctColor( @NonNull String acct, String nickname, int color_fg, int color_bg, String notification_sound ){ this.acct = acct; this.nickname = nickname; this.color_fg = color_fg; @@ -186,23 +186,24 @@ public class AcctColor { } public static void clearMemoryCache(){ - mMemoryCache.evictAll (); + mMemoryCache.evictAll(); } private static final char CHAR_REPLACE = 0x328A; - @NonNull public static CharSequence getStringWithNickname( @NonNull Context context, int string_id , @NonNull String acct ){ + @NonNull + public static CharSequence getStringWithNickname( @NonNull Context context, int string_id, @NonNull String acct ){ AcctColor ac = load( acct ); - String name = ! TextUtils.isEmpty( ac.nickname ) ? Utils.sanitizeBDI( ac.nickname ) : acct ; - SpannableStringBuilder sb = new SpannableStringBuilder( context.getString( string_id,new String(new char[]{CHAR_REPLACE})) ); - for(int i=sb.length()-1;i>=0;--i){ + String name = ! TextUtils.isEmpty( ac.nickname ) ? Utils.sanitizeBDI( ac.nickname ) : acct; + 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){ + 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){ + if( ac.color_bg != 0 ){ sb.setSpan( new BackgroundColorSpan( ac.color_bg ), i, i + name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ); } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/MutedApp.java b/app/src/main/java/jp/juggler/subwaytooter/table/MutedApp.java index 5097342b..79677a47 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/MutedApp.java +++ b/app/src/main/java/jp/juggler/subwaytooter/table/MutedApp.java @@ -14,6 +14,7 @@ public class MutedApp { private static final LogCategory log = new LogCategory( "MutedApp" ); public static final String table = "app_mute"; + public static final String COL_ID = "_id"; public static final String COL_NAME = "name"; private static final String COL_TIME_SAVE = "time_save"; diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/MutedWord.java b/app/src/main/java/jp/juggler/subwaytooter/table/MutedWord.java index 80bed614..6303bc83 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/MutedWord.java +++ b/app/src/main/java/jp/juggler/subwaytooter/table/MutedWord.java @@ -13,6 +13,7 @@ public class MutedWord { private static final LogCategory log = new LogCategory( "MutedWord" ); public static final String table = "word_mute"; + public static final String COL_ID = "_id"; public static final String COL_NAME = "name"; private static final String COL_TIME_SAVE = "time_save"; diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java index 60598df1..2cb1b09c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java +++ b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java @@ -72,10 +72,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext { public JSONObject token_info; public String visibility; public boolean confirm_boost; - + public boolean dont_hide_nsfw; public boolean dont_show_timeout; - + public boolean notification_mention; public boolean notification_boost; public boolean notification_favourite; @@ -87,15 +87,13 @@ public class SavedAccount extends TootAccount implements LinkClickContext { public boolean confirm_unfollow; public boolean confirm_post; - public String notification_tag; public String register_key; public long register_time; - - private final AtomicReference refInstance = new AtomicReference<>( null ); + private final AtomicReference< TootInstance > refInstance = new AtomicReference<>( null ); private static final long INSTANCE_INFORMATION_EXPIRE = 60000L * 5; - + // DBには保存しない public @Nullable TootInstance getInstance(){ TootInstance instance = refInstance.get(); @@ -104,10 +102,10 @@ public class SavedAccount extends TootAccount implements LinkClickContext { } return instance; } - public void setInstance(@NonNull TootInstance instance){ - refInstance.set(instance); - } + public void setInstance( @NonNull TootInstance instance ){ + refInstance.set( instance ); + } // アプリデータのインポート時に呼ばれる public static void onDBDelete( SQLiteDatabase db ){ @@ -154,7 +152,7 @@ public class SavedAccount extends TootAccount implements LinkClickContext { // 以下はDBスキーマ18で更新 + "," + COL_DONT_SHOW_TIMEOUT + " integer default 0" - + + ")" ); db.execSQL( "create index if not exists " + table + "_user on " + table + "(u)" ); @@ -268,7 +266,8 @@ public class SavedAccount extends TootAccount implements LinkClickContext { return acct.equals( "?@?" ); } - private static @Nullable SavedAccount parse( Context context, Cursor cursor ) throws JSONException{ + private static @Nullable + SavedAccount parse( Context context, Cursor cursor ) throws JSONException{ JSONObject src = new JSONObject( cursor.getString( cursor.getColumnIndex( COL_ACCOUNT ) ) ); SavedAccount dst = new SavedAccount(); dst = (SavedAccount) parse( context, dst, src, dst ); @@ -283,7 +282,7 @@ public class SavedAccount extends TootAccount implements LinkClickContext { dst.confirm_boost = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_CONFIRM_BOOST ) ) ); dst.dont_hide_nsfw = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_DONT_HIDE_NSFW ) ) ); dst.dont_show_timeout = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_DONT_SHOW_TIMEOUT ) ) ); - + dst.notification_mention = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_MENTION ) ) ); dst.notification_boost = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_BOOST ) ) ); dst.notification_favourite = ( 0 != cursor.getInt( cursor.getColumnIndex( COL_NOTIFICATION_FAVOURITE ) ) ); @@ -340,9 +339,9 @@ public class SavedAccount extends TootAccount implements LinkClickContext { public void updateTokenInfo( @Nullable JSONObject token_info ){ if( db_id == INVALID_ID ) throw new RuntimeException( "SavedAccount.updateTokenInfo missing db_id" ); - - if( token_info == null ) token_info = new JSONObject( ); - + + if( token_info == null ) token_info = new JSONObject(); + this.token_info = token_info; ContentValues cv = new ContentValues(); cv.put( COL_TOKEN, token_info.toString() ); @@ -504,7 +503,7 @@ public class SavedAccount extends TootAccount implements LinkClickContext { public static boolean hasRealAccount( @NonNull LogCategory log ){ try{ - Cursor cursor = App1.getDB().query( table, null, COL_USER + " NOT LIKE '?@%'", null , null, null, null, "1"); + Cursor cursor = App1.getDB().query( table, null, COL_USER + " NOT LIKE '?@%'", null, null, null, null, "1" ); try{ if( cursor.moveToNext() ){ return true; @@ -519,7 +518,6 @@ public class SavedAccount extends TootAccount implements LinkClickContext { return false; } - @SuppressWarnings("WeakerAccess") public @NonNull String getAccountHost( @Nullable String acct ){ if( acct != null ){ @@ -607,14 +605,6 @@ public class SavedAccount extends TootAccount implements LinkClickContext { return "?".equals( username ); } - private static final Pattern reAcctUrl = Pattern.compile( "\\Ahttps://([A-Za-z0-9.-]+)/@([A-Za-z0-9_]+)\\z" ); - - @Override public AcctColor findAcctColor( String url ){ - Matcher m = reAcctUrl.matcher( url ); - if( m.find() ) return AcctColor.load( m.group( 2 ) + "@" + m.group( 1 ) ); - return null; - } - public static long getCount(){ try{ Cursor cursor = App1.getDB().query( table, new String[]{ "count(*)" }, null, null, null, null, null ); @@ -683,17 +673,17 @@ public class SavedAccount extends TootAccount implements LinkClickContext { return true; } - private static final Comparator< SavedAccount > account_comparator = new Comparator< SavedAccount >() { + 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; - + 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; + 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 ); @@ -709,4 +699,14 @@ public class SavedAccount extends TootAccount implements LinkClickContext { String host = who.getAcctHost(); return host != null ? host : this.host; } + + // implements LinkClickContext + @Override @Nullable public AcctColor findAcctColor( @Nullable String url ){ + if( url != null ){ + Matcher m = TootAccount.reAccountUrl.matcher( url ); + if( m.find() ) return AcctColor.load( m.group( 2 ) + "@" + m.group( 1 ) ); + } + return null; + } + } diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/HTMLDecoder.java b/app/src/main/java/jp/juggler/subwaytooter/util/HTMLDecoder.java index 7f9b1a9d..83342d53 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/HTMLDecoder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/util/HTMLDecoder.java @@ -18,6 +18,7 @@ import java.util.regex.Pattern; import jp.juggler.subwaytooter.App1; import jp.juggler.subwaytooter.Pref; import jp.juggler.subwaytooter.R; +import jp.juggler.subwaytooter.api.entity.TootAccount; import jp.juggler.subwaytooter.api.entity.TootAttachment; import jp.juggler.subwaytooter.api.entity.TootMention; import jp.juggler.subwaytooter.table.SavedAccount; @@ -99,12 +100,11 @@ public class HTMLDecoder { private static final boolean DEBUG_HTML_PARSER = false; - static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)(?:\\z|\\?)" ); static HashSet< String > block_tag; private static void prepareTagInformation(){ - synchronized( reUserPage ){ + synchronized( log ){ if( block_tag == null ){ block_tag = new HashSet<>(); block_tag.add( "div" ); @@ -273,7 +273,7 @@ public class HTMLDecoder { if( ! display_url.startsWith( "http" ) ){ if( display_url.startsWith( "@" ) && href != null && App1.pref.getBoolean( Pref.KEY_MENTION_FULL_ACCT, false ) ){ // メンションをfull acct にする - Matcher m = reUserPage.matcher( href ); + Matcher m = TootAccount.reAccountUrl.matcher( href ); if( m.find() ){ return "@" + m.group( 2 ) + "@" + m.group( 1 ); } diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/LinkClickContext.java b/app/src/main/java/jp/juggler/subwaytooter/util/LinkClickContext.java index 493d7ec9..f6603879 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/LinkClickContext.java +++ b/app/src/main/java/jp/juggler/subwaytooter/util/LinkClickContext.java @@ -1,8 +1,10 @@ package jp.juggler.subwaytooter.util; +import android.support.annotation.Nullable; + import jp.juggler.subwaytooter.table.AcctColor; public interface LinkClickContext { - - AcctColor findAcctColor( String url ); + + @Nullable AcctColor findAcctColor( @Nullable String url ); } diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java b/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java index dedb7f9b..a9490554 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java +++ b/app/src/main/java/jp/juggler/subwaytooter/util/MyClickableSpan.java @@ -29,7 +29,7 @@ public class MyClickableSpan extends ClickableSpan { @NonNull LinkClickContext lcc , @NonNull String text , @NonNull String url - , AcctColor ac + , @Nullable AcctColor ac , @Nullable Object tag ){ this.lcc = lcc;