diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9ce22b2f..8c531213 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -153,6 +153,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
index 1a3f57a7..4aed6713 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java
@@ -31,6 +31,8 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import com.yasesprox.android.transcommusdk.TransCommuActivity;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -311,7 +313,7 @@ public class ActMain extends AppCompatActivity
if( requestCode == REQUEST_APP_SETTING ){
showFooterColor();
}
-
+
super.onActivityResult( requestCode, resultCode, data );
}
@@ -469,6 +471,11 @@ public class ActMain extends AppCompatActivity
}else if( id == R.id.nav_muted_word ){
startActivity( new Intent( this, ActMutedWord.class ) );
+// }else if( id == R.id.nav_translation ){
+// Intent intent = new Intent(this, TransCommuActivity.class);
+// intent.putExtra(TransCommuActivity.APPLICATION_CODE_EXTRA, "FJlDoBKitg");
+// this.startActivity(intent);
+//
// Handle the camera action
// }else if( id == R.id.nav_gallery ){
//
@@ -751,21 +758,71 @@ public class ActMain extends AppCompatActivity
// プロフURL
if( "https".equals( uri.getScheme() ) ){
if( uri.getPath().startsWith( "/@" ) ){
+ // ステータスをアプリ内で開く
+ Matcher m = reStatusPage.matcher( uri.toString() );
+ 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 );
+
+ ArrayList< SavedAccount > account_list_same_host = new ArrayList<>();
+
+ for( SavedAccount a : SavedAccount.loadAccountList( log ) ){
+ if( host.equalsIgnoreCase( a.host ) ){
+ account_list_same_host.add( a );
+ }
+ }
+
+ // ソートする
+ Collections.sort( account_list_same_host, 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 ) );
+ }
+ } );
+
+
+ if( account_list_same_host.isEmpty() ){
+ account_list_same_host.add( addPseudoAccount( host ) );
+ }
+
+
+ AccountPicker.pick( this, true, true
+ , getString( R.string.open_status_from )
+ , account_list_same_host
+ , new AccountPicker.AccountPickerCallback() {
+ @Override public void onAccountPicked( final SavedAccount ai ){
+ openStatus( ai, status_id );
+ }
+ } );
+
+ }catch( Throwable ex ){
+ Utils.showToast( this, ex, "can't parse status id." );
+ }
+ return;
+ }
+
// ユーザページをアプリ内で開く
- Matcher m = reUserPage.matcher( uri.toString() );
+ m = reUserPage.matcher( uri.toString() );
if( m.find() ){
// https://mastodon.juggler.jp/@SubwayTooter
final String host = m.group( 1 );
final String user = Uri.decode( m.group( 2 ) );
- ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
+
ArrayList< SavedAccount > account_list_same_host = new ArrayList<>();
-
- for( SavedAccount a : account_list ){
+ for( SavedAccount a : SavedAccount.loadAccountList( log ) ){
if( host.equalsIgnoreCase( a.host ) ){
account_list_same_host.add( a );
}
}
+ // ソートする
+ Collections.sort( account_list_same_host, 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 ) );
+ }
+ } );
+
if( account_list_same_host.isEmpty() ){
account_list_same_host.add( addPseudoAccount( host ) );
}
@@ -1058,10 +1115,6 @@ public class ActMain extends AppCompatActivity
} );
}
- public void performConversation( SavedAccount access_info, TootStatus status ){
- addColumn( access_info, Column.TYPE_CONVERSATION, status.id );
- }
-
private void performAddTimeline( boolean bAllowPseudo, final int type, final Object... args ){
AccountPicker.pick( this, bAllowPseudo, true
, getString( R.string.account_picker_add_timeline_of, Column.getColumnTypeName( this, type ) )
@@ -1080,10 +1133,6 @@ public class ActMain extends AppCompatActivity
} );
}
- public void openHashTag( SavedAccount access_info, String tag ){
- addColumn( access_info, Column.TYPE_HASHTAG, tag );
- }
-
public void performMuteApp( @NonNull TootApplication application ){
MutedApp.save( application.name );
for( Column column : pager_adapter.column_list ){
@@ -1170,7 +1219,8 @@ public class ActMain extends AppCompatActivity
}
static final Pattern reHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)\\z" );
- static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#]+)\\z" );
+ static final Pattern reUserPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)\\z" );
+ static final Pattern reStatusPage = Pattern.compile( "\\Ahttps://([^/]+)/@([^?#/]+)/(\\d+)\\z" );
public void openChromeTab( final SavedAccount access_info, final String url, boolean noIntercept ){
try{
@@ -1192,6 +1242,26 @@ public class ActMain extends AppCompatActivity
}
}
+ // ステータスページをアプリから開く
+ 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 );
+ if( host.equalsIgnoreCase( access_info.host ) ){
+ openStatus( access_info, status_id );
+ return;
+ }else{
+ openStatusOtherInstance( 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() ){
@@ -1210,6 +1280,8 @@ public class ActMain extends AppCompatActivity
} );
return;
}
+
+
}
try{
@@ -1236,6 +1308,67 @@ public class ActMain extends AppCompatActivity
}
}
+ public void openStatus( @NonNull SavedAccount access_info, @NonNull TootStatus status ){
+ openStatus( access_info, status.id );
+ }
+
+ public void openStatus( @NonNull SavedAccount access_info, long status_id ){
+ addColumn( access_info, Column.TYPE_CONVERSATION, status_id );
+ }
+
+ private void openStatusOtherInstance( final SavedAccount access_info, final String url, final String host, final long status_id ){
+ ActionsDialog dialog = new ActionsDialog();
+
+ // ブラウザで表示する
+ dialog.addAction( getString( R.string.open_web_on_host, host ), new Runnable() {
+ @Override public void run(){
+ openChromeTab( access_info, url, true );
+ }
+ } );
+
+ // 同タンスのアカウント
+ ArrayList< SavedAccount > account_list = new ArrayList<>( );
+ for( SavedAccount a: SavedAccount.loadAccountList( log ) ){
+ if( host.equalsIgnoreCase( a.host ) ){
+ account_list.add(a);
+ }
+ }
+
+ // ソートする
+ 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 ) );
+ }
+ } );
+
+ for( SavedAccount a : account_list ){
+ final SavedAccount _a = a;
+ dialog.addAction( getString( R.string.open_in_account, a.acct ), new Runnable() {
+ @Override public void run(){
+ openStatus( _a, status_id );
+ }
+ } );
+ }
+
+ // アカウントがないなら、疑似ホストを作る選択肢
+ if( account_list .isEmpty() ){
+ dialog.addAction( getString( R.string.open_in_pseudo_account, "?@" + host ), new Runnable() {
+ @Override public void run(){
+ SavedAccount sa = addPseudoAccount( host );
+ if( sa != null ){
+ openStatus( sa, status_id );
+ }
+ }
+ } );
+ }
+
+ dialog.show( this, getString( R.string.open_status_from ) );
+ }
+
+ public void openHashTag( SavedAccount access_info, String tag ){
+ addColumn( access_info, Column.TYPE_HASHTAG, tag );
+ }
+
// 他インスタンスのハッシュタグの表示
private void openHashTagOtherInstance( final SavedAccount access_info, final String url, final String host, final String tag ){
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActText.java b/app/src/main/java/jp/juggler/subwaytooter/ActText.java
index 8bd12122..07dfc85b 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActText.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActText.java
@@ -63,6 +63,7 @@ public class ActText extends AppCompatActivity implements View.OnClickListener {
@Override protected void onCreate( @Nullable Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
+ App1.setActivityTheme( this, false );
initUI();
diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java
index 3b5955a2..a95cc1be 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/Column.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java
@@ -36,6 +36,7 @@ import jp.juggler.subwaytooter.table.MutedApp;
import jp.juggler.subwaytooter.table.MutedWord;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.table.UserRelation;
+import jp.juggler.subwaytooter.util.BucketList;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.MyListView;
import jp.juggler.subwaytooter.util.ScrollPosition;
@@ -706,7 +707,7 @@ class Column {
String task_progress;
- final ArrayList< Object > list_data = new ArrayList<>();
+ final BucketList< Object > list_data = new BucketList<>();
private static boolean hasMedia( TootStatus status ){
if( status == null ) return false;
@@ -1815,9 +1816,7 @@ class Column {
}
int added = list_new.size();
- list_new.addAll( list_data );
- list_data.clear();
- list_data.addAll( list_new );
+ list_data.addAll( 0, list_new );
fireShowContent();
if( status_index >= 0 && refresh_after_toot == Pref.RAT_REFRESH_SCROLL ){
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemListAdapter.java b/app/src/main/java/jp/juggler/subwaytooter/ItemListAdapter.java
index 30cc9a4f..7a3d6a65 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ItemListAdapter.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/ItemListAdapter.java
@@ -5,14 +5,14 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
-import java.util.ArrayList;
+import java.util.List;
import jp.juggler.subwaytooter.util.MyListView;
class ItemListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final Column column;
private final ActMain activity;
- private final ArrayList< Object > list;
+ private final List< Object > list;
ItemListAdapter( ActMain activity,Column column ){
this.activity = activity;
diff --git a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
index 591220a1..f3edcbe5 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
+++ b/app/src/main/java/jp/juggler/subwaytooter/StatusButtons.java
@@ -106,7 +106,7 @@ class StatusButtons implements View.OnClickListener {
if( close_window != null ) close_window.dismiss();
switch( v.getId() ){
case R.id.btnConversation:
- activity.performConversation( access_info, status );
+ activity.openStatus( access_info, status );
break;
case R.id.btnReply:
if( access_info.isPseudo() ){
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/BucketList.java b/app/src/main/java/jp/juggler/subwaytooter/util/BucketList.java
new file mode 100644
index 00000000..652dc237
--- /dev/null
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/BucketList.java
@@ -0,0 +1,224 @@
+package jp.juggler.subwaytooter.util;
+
+import android.support.annotation.NonNull;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+
+public class BucketList < E >
+ extends AbstractList< E >
+ implements Iterable< E >, RandomAccess
+{
+
+ private static final int DEFAULT_BUCKET_CAPACITY = 1024;
+
+ @SuppressWarnings("WeakerAccess")
+ public BucketList( int initialStep ){
+ mStep = initialStep;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public BucketList(){
+ this( DEFAULT_BUCKET_CAPACITY );
+ }
+
+
+ private static class Bucket < E > extends ArrayList< E > {
+ int total_start;
+ int total_end;
+
+ Bucket( int capacity ){
+ super( capacity );
+ }
+ }
+
+ private final ArrayList< Bucket< E > > groups = new ArrayList<>();
+ private int mSize;
+ private int mStep;
+
+ private void updateIndex(){
+ int n = 0;
+ for( Bucket< E > bucket : groups ){
+ bucket.total_start = n;
+ bucket.total_end = n = n + bucket.size();
+ }
+ mSize = n;
+ }
+
+ private static class BucketPos {
+ int group_index;
+ int bucket_index;
+
+ BucketPos(){
+ }
+
+ BucketPos( int gi, int bi ){
+ this.group_index = gi;
+ this.bucket_index = bi;
+ }
+ }
+
+ private static final ThreadLocal< BucketPos > pos_internal = new ThreadLocal< BucketPos >() {
+ @Override protected BucketPos initialValue(){
+ return new BucketPos();
+ }
+ };
+
+ private BucketPos findPos( BucketPos dst, int total_index ){
+
+ if( total_index < 0 || total_index >= mSize ){
+ throw new ArrayIndexOutOfBoundsException( "findPos: bad index=" + total_index + ", size=" + mSize );
+ }
+
+ // binary search
+ int gs = 0;
+ int ge = groups.size();
+ for( ; ; ){
+ int gi = ( gs + ge ) >> 1;
+ Bucket< E > group = groups.get( gi );
+ if( total_index < group.total_start ){
+ ge = gi;
+ }else if( total_index >= group.total_end ){
+ gs = gi + 1;
+ }else{
+ if( dst == null ) dst = new BucketPos();
+ dst.group_index = gi;
+ dst.bucket_index = total_index - group.total_start;
+ return dst;
+ }
+ }
+ }
+
+ @Override
+ public void clear(){
+ groups.clear();
+ mSize = 0;
+ }
+
+ // 末尾への追加
+ @Override
+ public boolean addAll( @NonNull Collection< ? extends E > c ){
+ int c_size = c.size();
+ if( c_size == 0 ) return false;
+
+ // 最後のバケツに収まるなら、最後のバケツの中に追加する
+ if( groups.size() > 0 ){
+ Bucket< E > bucket = groups.get( groups.size() - 1 );
+ if( bucket.size() + c_size <= mStep ){
+ bucket.addAll( c );
+ bucket.total_end += c_size;
+ mSize += c_size;
+ return true;
+ }
+ }
+ // 新しいバケツを作って、そこに追加する
+ Bucket< E > bucket = new Bucket<>( mStep );
+ bucket.addAll( c );
+ bucket.total_start = mSize;
+ bucket.total_end = mSize + c_size;
+ mSize += c_size;
+ groups.add( bucket );
+ return true;
+ }
+
+ @Override
+ public boolean addAll( int index, @NonNull Collection< ? extends E > c ){
+
+ // indexが終端なら、終端に追加する
+ // バケツがカラの場合もここ
+ if( index == mSize ){
+ return addAll( c );
+ }
+
+ int c_size = c.size();
+ if( c_size == 0 ) return false;
+
+ BucketPos pos = findPos( pos_internal.get(), index );
+ Bucket< E > bucket = groups.get( pos.group_index );
+
+ // 挿入位置がバケツの先頭ではないか、バケツのサイズに問題がないなら
+ if( pos.bucket_index > 0 || bucket.size() + c_size <= mStep ){
+ // バケツの中に挿入する
+ bucket.addAll( pos.bucket_index, c );
+ }else{
+ // 新しいバケツを作って、そこに追加する
+ bucket = new Bucket<>( mStep );
+ bucket.addAll( c );
+ groups.add( pos.group_index, bucket );
+ }
+ updateIndex();
+ return true;
+ }
+
+ public E remove( int index ){
+ BucketPos pos = findPos( pos_internal.get(), index );
+ Bucket< E > bucket = groups.get( pos.group_index );
+ E data = bucket.remove( pos.bucket_index );
+ updateIndex();
+ return data;
+ }
+
+ public int size(){
+ return mSize;
+ }
+
+ public boolean isEmpty(){
+ return 0 == mSize;
+ }
+
+ public E get( int idx ){
+ BucketPos pos = findPos( pos_internal.get(), idx );
+ return groups.get( pos.group_index ).get( pos.bucket_index );
+ }
+
+ private class MyIterator implements Iterator< E > {
+ private final BucketPos pos; // indicates next read point
+
+ MyIterator(){
+ pos = new BucketPos( 0, 0 );
+ }
+
+ @Override public boolean hasNext(){
+ for( ; ; ){
+ if( pos.group_index >= groups.size() ){
+ return false;
+ }
+ Bucket< E > bucket = groups.get( pos.group_index );
+ if( pos.bucket_index >= bucket.size() ){
+ pos.bucket_index = 0;
+ ++ pos.group_index;
+ continue;
+ }
+ return true;
+ }
+ }
+
+ @Override public E next(){
+ for( ; ; ){
+ if( pos.group_index >= groups.size() ){
+ throw new NoSuchElementException();
+ }
+ Bucket< E > bucket = groups.get( pos.group_index );
+ if( pos.bucket_index >= bucket.size() ){
+ pos.bucket_index = 0;
+ ++ pos.group_index;
+ continue;
+ }
+ return bucket.get( pos.bucket_index++ );
+ }
+ }
+
+ @Override public void remove(){
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @NonNull @Override public Iterator< E > iterator(){
+ return new MyIterator();
+ }
+
+}
diff --git a/app/src/main/res/layout/lv_status.xml b/app/src/main/res/layout/lv_status.xml
index d2ac4cf3..df19c208 100644
--- a/app/src/main/res/layout/lv_status.xml
+++ b/app/src/main/res/layout/lv_status.xml
@@ -26,6 +26,7 @@
android:layout_height="32dp"
android:layout_marginEnd="4dp"
android:scaleType="fitEnd"
+ android:importantForAccessibility="no"
/>
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:orientation="horizontal"
+ >
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
@@ -194,175 +195,180 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
- Footer color
Tab background color
Tab divider color
+ Help translation
+ Open status from
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index e53091e6..af099518 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -1,299 +1,591 @@
+
Subway Tooter
-
+
設定
+
アカウントの追加
+
インスタンス
+
メールアドレス
+
パスワード
+
OK
+
キャンセル
+
インスタンスを指定してください
+
メールを指定してください
+
パスワードを指定してください
-
+
通信エラー
+
応答エラー %1$s
+
APIエラー %1$s
-
+
%1$sにこのアプリを登録しています…
+
アクセストークンの取得中…
+
取得中… %1$s
+
左下のメニューボタンを押してアカウントまたはカラムを追加してください
+
ホーム
+
ローカルタイムライン
+
連合タイムライン
+
アカウントの設定からアクセストークンの更新を行ってください
+
キャンセルされました
+
アカウントの選択
+
アカウントを確認できました
+
返信
+
その他
+
カラムを閉じる
+
サムネイル
+
再読み込み
+
お気に入り
+
通知
+
通報
+
あなたの発言
+
%1$s の発言
+
フォロー
+
フォロー中
+
フォロワー
+
発言
+
隠す
+
NSFW
+
タップで表示
+
見る
+
Tootの送信
+
コンテントワーニング
+
アカウントがありません。事前にアカウントの追加を行ってください
+
(未選択)
+
コンテントワーニングをここに入力
+
発言の本文をここに入力
+
ステータス
+
リストは空です
+
発言者
+
アカウントの選択
+
APIの応答がJSONではありません
+
最大4個までです
+
アカウントを選択してください
+
ログインに失敗
+
最大8MBまでです
+
公開
+
未収載
+
非公開
+
ダイレクト
+
公開範囲の選択
+
この添付データを除去しますか?
+
発言の本文を入力してください
+
コンテントワーニングを入力してください
+
直前の操作が完了するまでお待ちください
+
お気に入りを外さないとブーストを外せません?
+
確認
+
この発言を %1$s からブーストしますか? 全てのフォロワーとあなたのプロフィールページに公開されます
+
%1$sがお気に入りに追加しました
+
%1$sがブーストしました
+
%1$sからの返信
+
%1$sにフォローされました
+
アカウント
+
アカウントの設定
+
設定
+
アプリの設定
+
カラム一覧
+
アクセストークンの更新
+
アカウントの削除
+
操作
+
発言の公開範囲の既定値
+
ブースト前に確認
+
NSFWな添付データを隠さない
+
ユーザ
+
アカウントをこのアプリから除去しますか?関連するカラムはすべて除去されます
+
ユーザ名が一致しません
+
%1$sのアクセストークンを更新しました
+
戻るボタンでカラム一覧を開く
+
先にアカウントの追加を行ってください
+
確認なしでカラムを閉じる
+
並べ替え用のつまみ
+
削除
+
スワイプで削除します。並べ替えと削除は戻るまたは選択した時に反映されます
+
最後に選択した
+
%1$d/%2$d
+
この発言への返信:
+
添付ファイルがある状態ではアカウントを変更できません
+
カラム
+
会話ビュー
+
会話の流れ(id:%1$d)
+
インスタンスには\'/\'を含めずに、ホスト名だけを指定してください
+
メールアドレスが不十分です。\'@\'や\'.\'がありません
+
例: mastodon.social
+
例: your@e-mail.address
+
フォロー解除
+
フォローできました
+
フォロー解除できました
+
ミュート
+
ミュート解除
+
ブロック
+
ブロック解除
+
通報
+
鍵つきユーザをフォローできません
+
ミュート解除できました
+
ミュートできました
+
ブロック解除できました
+
ブロックできました
+
通報する理由を書いてください
+
通報する理由を書いてください
+
通報できました
+
この人への発言
+
ハッシュタグ #%1$s
+
%1$sから開く
+
Webページを開く
+
%1$sのWebページを開く
+
検索:%1$s
+
検索
+
他タンスのアカウントも探す
+
\?
+
このカラムを閉じますか?
-
+
別のアカウントでフォロー
+
(同タンスの)別アカウントでお気に入り
+
(同タンスの)別アカウントでブースト
+
このアプリについて
+
ストアで評価やレビューをお願いします!
+
ストアで評価
+
開発継続のために寄付をお願いします!
+
バージョン %1$s
+
OSSライセンス
+
https://%1$s/ を開く
+
返信
+
%1$d件の通知
+
ブースト
+
お気に入り
+
アプリを終了
+
毎回尋ねる
+
カラム一覧を開く
+
戻るボタンの動作
+
添付画像を開く時に自タンスURLを重視
+
FastScrollerを無効にする(アプリ再起動が必要)
+
削除できました
+
開発者
+
添付データあり
+
ギャップを読む
+
別の処理を行っています
+
ブロックしたユーザ
+
リストの終端
+
ミュートしたユーザ
+
テキストをアプリに送る
+
Date
+
From-Acct
+
From-Name
+
Content-Warning
+
Status-URL
+
保護されたカラムは閉じられません
+
保護(カラムを閉じない)
+
暗い
+
明るい
+
UIテーマ(アプリ再起動が必要)
+
ブーストを表示しない
+
返信を表示しない
+
正規表現エラー
+
正規表現フィルタ(上級者向け)
+
ユーザへの通知データがタンスのサーバから削除されます。よろしいですか?
+
通知の削除
+
%1$s を検索
+
公開範囲をアカウントの既定値に狭めました
+
contributor
+
Thanks for %1$s
+
簡略表示(アプリ再起動が必要)
+
アプリはミュートされます
+
アプリ: %1$s
+
ブーストできました
+
お気に入りにしました
+
スワイプで解除。解除した後はカラムをリロードすると再表示されます
+
\"%1$s\" アプリをミュート
+
ミュートしたアプリ
+
%1$s からのフォローリクエストを許可しました
+
フォローリクエストの却下
+
フォローリクエストの許可
+
%1$s からのフォローリクエストを却下しました
+
フォローリクエスト一覧
+
LED
+
通知オプション
+
音
+
振動
+
閉じる
+
疑似アカウントでは利用できません
+
疑似アカウント(ログインなし、公開データを読めるだけ)
+
サーバを確認しました
+
保護されたカラムを戻るボタンで確認なしで閉じようとしたらアプリを終了する
+
しない
+
添付画像のリサイズ(JPEG,PNG)
+
長辺 %1$d ピクセル
+
カメラで撮影
+
画像を選択
+
添付メディアをアップロードできました
+
添付メディアのアップロード中です…
+
ストレージ書き込み権限がありません
+
別アカウントでメッセージを送る
+
メッセージを送る
+
(同タンスの)別アカウントでプロフを開く
+
プロフを開く
+
ユーザへのアクション
+
発言へのアクション
+
ユーザ \'%1$s\' をブロックします。よろしいですか?
+
ユーザ \'%1$s\' をミュートします。よろしいですか?
+
この発言を削除します。よろしいですか?
+
Acct
+
背景色
+
Color
+
取り消し
+
編集
+
色
+
通称 (指定するとAcctの代わりに表示されます)
+
プレビュー
+
リセット
+
保存
+
文字色
+
通称と色
+
通称と色の変更はカラムをリロードした後に反映されます
+
確認
+
鍵つきユーザのフォロー
+
フォローリクエストを申請しました
+
フォローボタンをボタンバーに表示する(アプリ再起動が必要)
+
%2$s から鍵付きユーザ %1$s にフォローリクエストを送りますか?
+
%2$s から %1$s をフォローしますか?
+
%1$s から投稿します。よろしいですか?
+
%2$s から %1$s をフォロー解除しますか?
+
次回から確認しない
+
フォローリクエストを送信者から解除することはできないようです…
+
プロフィール
+
%1$s のプロフィール
+
あなたです
+
発言をブーストした人
+
更新しない
+
発言をお気に入りした人
+
発言後の更新
+
更新するがスクロールしない
+
更新して発言までスクロール
+
どのアカウントで %1$s を開きますか?
+
どのアカウントでブーストしますか?
+
どのアカウントでお気に入りしますか?
+
どのアカウントでフォローしますか?
+
どのアカウントの設定を開きますか?
+
どのアカウントでユーザ %1$s のプロフィールを確認しますか?
+
どのアカウントでトゥートしますか?
+
会話の流れ
+
ハッシュタグ
+
QRコードの作成中…
+
QRコード
+
消去
+
テキストの消去
+
テキストとメディアの消去
+
添付メディア
+
トゥート
+
公開範囲
+
ユーザ画像を角丸にしない(アプリ再起動が必要)
+
色と背景
+
カラム背景
+
カラムヘッダ
+
文字とアイコンの色
+
画像
+
画像の不透明度
+
メニュー
+
疑似アカウント %1$s で開く
+
画像の選択
+
メッセージにハッシュタグが含まれていますが、公開範囲が公開ではありません。ハッシュタグは公開メッセージに含まれる場合のみ検索されることができます。続けてもよろしいですか?
+
コピー
+
単語をミュート
+
ミュートした単語
+
Web検索
+
選択してコピー…
+
共有
+
単語をミュートしました
+
クリップボードにコピーしました
+
ボタン背景色
+
ボタン前景色
+
フッタの色
+
タブ背景色
+
タブ両端の区切りの色
-
+
+ ステータスを次のアカウントから開く
+ 翻訳に協力する
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7c9500ae..a3fba1cb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -296,4 +296,6 @@
Button foreground color
Tab background color
Tab divider color
+ Open status from
+ Help translation