- v1.4.5の変更で、通知をタップしても通知タブに移動しなくなっていたのを修正
- マストドン1.5.1以降のタンスで、フォロー済みユーザを再度リモートフォローしようとすると500エラーが表示される。メッセージを代替のものに変えた
This commit is contained in:
tateisu 2017-09-10 20:16:39 +09:00
parent 4e4a73042e
commit 4f5c9ef720
11 changed files with 190 additions and 131 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 26
versionCode 145
versionName "1.4.5-beta"
versionCode 146
versionName "1.4.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:name=".App1"

View File

@ -932,7 +932,7 @@ public class ActAccountSetting extends AppCompatActivity
);
return;
}
Utils.showToast( this, true, R.string.missing_storage_permission );
Utils.showToast( this, true, R.string.missing_permission_to_access_media );
}
@Override public void onRequestPermissionsResult(
@ -949,7 +949,7 @@ public class ActAccountSetting extends AppCompatActivity
){
openPicker( requestCode );
}else{
Utils.showToast( this, true, R.string.missing_storage_permission );
Utils.showToast( this, true, R.string.missing_permission_to_access_media );
}
break;
}

View File

@ -244,23 +244,13 @@ public class ActMain extends AppCompatActivity
// 投稿直後ならカラムの再取得を行う
refreshAfterPost();
// 外部から受け取ったUriの処理
Uri uri = ActCallback.last_uri.getAndSet( null );
if( uri != null ){
handleIntentUri( uri );
}
// 外部から受け取ったUriの処理
Intent intent = ActCallback.sent_intent.getAndSet( null );
if( intent != null ){
handleSentIntent( intent );
}
// 画面復帰時に再取得やストリーミング開始を行う
for( Column column : app_state.column_list ){
column.onStart( this );
}
// カラムの表示範囲インジケータを更新
updateColumnStripSelection( - 1, - 1f );
@ -299,6 +289,19 @@ public class ActMain extends AppCompatActivity
}else{
getWindow().clearFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
}
// 外部から受け取ったUriの処理
Uri uri = ActCallback.last_uri.getAndSet( null );
if( uri != null ){
handleIntentUri( uri );
}
// 外部から受け取ったUriの処理
Intent intent = ActCallback.sent_intent.getAndSet( null );
if( intent != null ){
handleSentIntent( intent );
}
}
@Override protected void onPause(){
@ -3498,7 +3501,8 @@ public class ActMain extends AppCompatActivity
}else if( bFollow && who.locked && result.response != null && result.response.code() == 422 ){
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
}else if(result.response !=null && result.response.code()==500){
Utils.showToast( ActMain.this, false, R.string.already_followed );
}else{
Utils.showToast( ActMain.this, false, result.error );
}
@ -3618,6 +3622,8 @@ public class ActMain extends AppCompatActivity
}else if( locked && result.response != null && result.response.code() == 422 ){
Utils.showToast( ActMain.this, false, R.string.cant_follow_locked_user );
}else if(result.response !=null && result.response.code()==500){
Utils.showToast( ActMain.this, false, R.string.already_followed );
}else{
Utils.showToast( ActMain.this, false, result.error );
}

View File

@ -53,8 +53,6 @@ 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;
import java.util.Locale;
import jp.juggler.subwaytooter.api.TootApiClient;
@ -179,6 +177,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
private static final int REQUEST_CODE_ATTACHMENT = 1;
private static final int REQUEST_CODE_CAMERA = 2;
private static final int REQUEST_CODE_MUSHROOM = 3;
private static final int REQUEST_CODE_VIDEO = 4;
@Override
protected void onActivityResult( int requestCode, int resultCode, Intent data ){
@ -222,6 +221,13 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
addAttachment( uri, type );
}
}
}else if( requestCode == REQUEST_CODE_VIDEO && resultCode == RESULT_OK ){
Uri uri = ( data == null ? null : data.getData() );
if( uri != null ){
String type = getContentResolver().getType( uri );
addAttachment( uri, type );
}
}else if( requestCode == REQUEST_CODE_MUSHROOM && resultCode == RESULT_OK ){
String text = data.getStringExtra( "replace_key" );
applyMushroomResult( text );
@ -954,12 +960,18 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
}
void openAttachment(){
int permissionCheck = ContextCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE );
if( permissionCheck != PackageManager.PERMISSION_GRANTED ){
preparePermission();
return;
}
permissionCheck = ContextCompat.checkSelfPermission( this, Manifest.permission.CAMERA );
if( permissionCheck != PackageManager.PERMISSION_GRANTED ){
preparePermission();
return;
}
ActionsDialog a = new ActionsDialog();
a.addAction( getString( R.string.image_pick ), new Runnable() {
@ -972,6 +984,12 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
performCamera();
}
} );
// a.addAction( getString( R.string.video_capture ), new Runnable() {
// @Override public void run(){
// performCameraVideo();
// }
// } );
a.show( this, null );
}
@ -1002,6 +1020,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
}
}
interface InputStreamOpener {
InputStream open() throws IOException;
@ -1276,7 +1295,16 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
Utils.showToast( this, ex, "opening camera app failed." );
}
}
private void performCameraVideo(){
try{
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
startActivityForResult(takeVideoIntent, REQUEST_CODE_VIDEO);
}catch( Throwable ex ){
log.trace( ex );
Utils.showToast( this, ex, "opening video app failed." );
}
}
private static final int PERMISSION_REQUEST_CODE = 1;
private void preparePermission(){
@ -1284,12 +1312,15 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions( this
, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }
, new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA,
}
, PERMISSION_REQUEST_CODE
);
return;
}
Utils.showToast( this, true, R.string.missing_storage_permission );
Utils.showToast( this, true, R.string.missing_permission_to_access_media );
}
@Override public void onRequestPermissionsResult(
@ -1299,13 +1330,16 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
){
switch( requestCode ){
case PERMISSION_REQUEST_CODE:
// If request is cancelled, the result arrays are empty.
if( grantResults.length > 0 &&
grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED
){
openAttachment();
boolean bNotGranted = false;
for(int i=0,ie=permissions.length;i<ie;++i){
if( grantResults[ i ] != PackageManager.PERMISSION_GRANTED ){
bNotGranted = true;
}
}
if( bNotGranted ){
Utils.showToast( this, true, R.string.missing_permission_to_access_media );
}else{
Utils.showToast( this, true, R.string.missing_storage_permission );
openAttachment();
}
break;
}

View File

@ -54,8 +54,7 @@ import jp.juggler.subwaytooter.view.MyListView;
import jp.juggler.subwaytooter.util.ScrollPosition;
import jp.juggler.subwaytooter.util.Utils;
@SuppressWarnings("WeakerAccess")
class Column implements StreamReader.Callback {
@SuppressWarnings("WeakerAccess") class Column implements StreamReader.Callback {
private static final LogCategory log = new LogCategory( "Column" );
interface Callback {
@ -222,7 +221,7 @@ class Column implements StreamReader.Callback {
// インスタンス情報カラムに表示するインスタンス情報
// (SavedAccount中のインスタンス情報とは異なるので注意)
TootInstance instance_information;
ScrollPosition scroll_save;
Column( @NonNull AppState app_state, @NonNull SavedAccount access_info, @NonNull Callback callback, int type, Object... params ){
@ -255,7 +254,7 @@ class Column implements StreamReader.Callback {
case TYPE_SEARCH_PORTAL:
this.search_query = (String) getParamAt( params, 0 );
break;
case TYPE_INSTANCE_INFORMATION:
this.instance_uri = (String) getParamAt( params, 0 );
break;
@ -308,11 +307,11 @@ class Column implements StreamReader.Callback {
case TYPE_SEARCH_PORTAL:
item.put( KEY_SEARCH_QUERY, search_query );
break;
case TYPE_INSTANCE_INFORMATION:
item.put( KEY_INSTANCE_URI, instance_uri );
break;
}
// 以下は保存には必要ないがカラムリスト画面で使う
@ -1158,7 +1157,7 @@ class Column implements StreamReader.Callback {
return null;
}
@NonNull static final VersionString version_1_6 = new VersionString("1.6");
@NonNull static final VersionString version_1_6 = new VersionString( "1.6" );
void startLoading(){
cancelLastTask();
@ -1191,80 +1190,80 @@ class Column implements StreamReader.Callback {
TootInstance instance_tmp;
TootApiResult getInstanceInformation( @NonNull TootApiClient client ,@Nullable String instance_name){
TootApiResult getInstanceInformation( @NonNull TootApiClient client, @Nullable String instance_name ){
instance_tmp = null;
if( instance_name != null ) client.setInstance( instance_name );
TootApiResult result = client.request( "/api/v1/instance" );
if( result != null && result.object != null ){
instance_tmp = TootInstance.parse( result.object );
}
return result;
}
ArrayList< Object > list_pinned;
TootApiResult getStatusesPinned( TootApiClient client, String path_base ){
TootApiResult result = client.request( path_base );
if( result != null && result.array != null ){
//
TootStatus.List src = TootStatus.parseList( context, access_info, result.array ,true);
TootStatus.List src = TootStatus.parseList( context, access_info, result.array, true );
for(TootStatus status : src ){
log.d("pinned: %s %s",status.id, status.decoded_content);
for( TootStatus status : src ){
log.d( "pinned: %s %s", status.id, status.decoded_content );
}
list_pinned = new ArrayList<>( src.size() );
addWithFilter( list_pinned, src );
// 1.6rc では以下の理由により40overの固定トゥートを取得することは困難である
// - max_idを指定せずにAPIで取得すると適当な件数のリストが返ってくるソート順はpinした日時max_idはリスト中の最後の要素のIDを返す
// - max_idを指定してAPIで取得するとステータスIDがmax_idより小さい&pinされているトゥートをpin日時順にソートしたものが返ってくる
// - max_idはpin日時を考慮していないのだからページング用のパラメータとしては全く不適切である
// - 取得できるステータスにはpinされた日時は含まれない
// //
// // pinステータスは独自にページ管理する
// long time_start = SystemClock.elapsedRealtime();
// String max_id = parseMaxId( result );
// char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
// for( ; ; ){
//
// if( client.isCancelled() ){
// log.d( "loading-statuses-pinned: cancelled." );
// break;
// }
// if( max_id == null ){
// log.d( "loading-statuses-pinned: max_id is null." );
// break;
// }
// if( src.isEmpty() ){
// log.d( "loading-statuses-pinned: previous response is empty." );
// break;
// }
// if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// log.d( "loading-statuses-pinned: timeout." );
// break;
// }
//
// String path = path_base + delimiter + "max_id=" + max_id;
// TootApiResult result2 = client.request( path );
// if( result2 == null || result2.array == null ){
// log.d( "loading-statuses-pinned: error or cancelled." );
// break;
// }
//
// src = TootStatus.parseList( context, access_info, result2.array ,true);
// for(TootStatus status : src ){
// log.d("pinned: %s %s",status.id, status.decoded_content);
// }
//
// addWithFilter( list_pinned, src );
//
// // pinnedステータスは独自にページ管理する
// max_id = parseMaxId( result2 );
// }
// - max_idを指定せずにAPIで取得すると適当な件数のリストが返ってくるソート順はpinした日時max_idはリスト中の最後の要素のIDを返す
// - max_idを指定してAPIで取得するとステータスIDがmax_idより小さい&pinされているトゥートをpin日時順にソートしたものが返ってくる
// - max_idはpin日時を考慮していないのだからページング用のパラメータとしては全く不適切である
// - 取得できるステータスにはpinされた日時は含まれない
// //
// // pinステータスは独自にページ管理する
// long time_start = SystemClock.elapsedRealtime();
// String max_id = parseMaxId( result );
// char delimiter = ( - 1 != path_base.indexOf( '?' ) ? '&' : '?' );
// for( ; ; ){
//
// if( client.isCancelled() ){
// log.d( "loading-statuses-pinned: cancelled." );
// break;
// }
// if( max_id == null ){
// log.d( "loading-statuses-pinned: max_id is null." );
// break;
// }
// if( src.isEmpty() ){
// log.d( "loading-statuses-pinned: previous response is empty." );
// break;
// }
// if( SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT ){
// log.d( "loading-statuses-pinned: timeout." );
// break;
// }
//
// String path = path_base + delimiter + "max_id=" + max_id;
// TootApiResult result2 = client.request( path );
// if( result2 == null || result2.array == null ){
// log.d( "loading-statuses-pinned: error or cancelled." );
// break;
// }
//
// src = TootStatus.parseList( context, access_info, result2.array ,true);
// for(TootStatus status : src ){
// log.d("pinned: %s %s",status.id, status.decoded_content);
// }
//
// addWithFilter( list_pinned, src );
//
// // pinnedステータスは独自にページ管理する
// max_id = parseMaxId( result2 );
// }
}
log.d("getStatusesPinned: list size=%s",list_pinned==null? -1 : list_pinned.size() );
log.d( "getStatusesPinned: list size=%s", list_pinned == null ? - 1 : list_pinned.size() );
return result;
}
@ -1456,35 +1455,35 @@ class Column implements StreamReader.Callback {
return getStatuses( client, PATH_FEDERATE );
case TYPE_PROFILE:
parseAccount1( client, String.format( Locale.JAPAN, PATH_ACCOUNT, profile_id ) );
client.callback.publishApiProgress( "" );
switch( profile_tab ){
default:
case TAB_STATUS:
TootInstance instance = access_info.getInstance();
if( access_info.isPseudo() || instance == null ){
TootApiResult r2 = getInstanceInformation( client ,null );
TootApiResult r2 = getInstanceInformation( client, null );
if( instance_tmp != null ){
instance = instance_tmp;
access_info.setInstance( instance_tmp );
}
if( access_info.isPseudo() ) return r2;
}
{
String s = String.format( Locale.JAPAN, PATH_ACCOUNT_STATUSES, profile_id );
if( with_attachment ) s = s + "&only_media=1";
if( instance != null && instance.isEnoughVersion(version_1_6) ){
getStatusesPinned( client, s + "&pinned=1");
}
return getStatuses( client, s );
{
String s = String.format( Locale.JAPAN, PATH_ACCOUNT_STATUSES, profile_id );
if( with_attachment ) s = s + "&only_media=1";
if( instance != null && instance.isEnoughVersion( version_1_6 ) ){
getStatusesPinned( client, s + "&pinned=1" );
}
return getStatuses( client, s );
}
case TAB_FOLLOWING:
return parseAccountList( client,
@ -1533,7 +1532,7 @@ class Column implements StreamReader.Callback {
result = client.request(
String.format( Locale.JAPAN, PATH_STATUSES, status_id ) );
if( result == null || result.object == null ) return result;
TootStatus target_status = TootStatus.parse( context,access_info, result.object );
TootStatus target_status = TootStatus.parse( context, access_info, result.object );
if( target_status == null ){
return new TootApiResult( "TootStatus parse failed." );
}
@ -3223,42 +3222,44 @@ class Column implements StreamReader.Callback {
// 破棄されたカラムなら何もしない
if( is_dispose.get() ){
log.d( "onResume: column was disposed." );
log.d( "onStart: column was disposed." );
return;
}
// 未初期化なら何もしない
if( ! bFirstInitialized ){
log.d( "onResume: column is not initialized." );
log.d( "onStart: column is not initialized." );
return;
}
// 初期ロード中なら何もしない
if( bInitialLoading ){
log.d( "onResume: column is in initial loading." );
log.d( "onStart: column is in initial loading." );
return;
}
// 始端リフレッシュの最中だった
// リフレッシュ終了時に自動でストリーミング開始するはず
if( bRefreshingTop ){
// 始端リフレッシュの最中だった
// リフレッシュ終了時に自動でストリーミング開始するはず
log.d( "onResume: bRefreshingTop is true." );
}else if(
! bRefreshLoading
&& canAutoRefresh()
&& ! App1.getAppState( context ).pref.getBoolean( Pref.KEY_DONT_REFRESH_ON_RESUME, false )
&& ! dont_auto_refresh
log.d( "onStart: bRefreshingTop is true." );
return;
}
if( ! bRefreshLoading
&& canAutoRefresh()
&& ! App1.getAppState( context ).pref.getBoolean( Pref.KEY_DONT_REFRESH_ON_RESUME, false )
&& ! dont_auto_refresh
){
// リフレッシュしてからストリーミング開始
log.d( "onResume: start auto refresh." );
log.d( "onStart: start auto refresh." );
startRefresh( true, false, - 1L, - 1 );
}else if( column_type == TYPE_SEARCH || column_type == TYPE_SEARCH_PORTAL ){
// 検索カラムはリフレッシュもストリーミングもないがresumeのタイミングでリストの再描画を行いたい
// 検索カラムはリフレッシュもストリーミングもないが表示開始のタイミングでリストの再描画を行いたい
fireShowContent();
}else{
// ギャップつきでストリーミング開始
log.d( "onResume: start streaming with gap." );
log.d( "onStart: start streaming with gap." );
resumeStreaming( true );
}
}

View File

@ -135,7 +135,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
@ -229,7 +229,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
@ -295,7 +295,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
//noinspection ConstantConditions
@ -377,7 +377,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
JSONObject token_info;
@ -425,7 +425,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
@ -490,7 +490,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
@ -535,7 +535,7 @@ public class TootApiClient {
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( url + ": " + context.getString( R.string.network_error_arg, response ) );
return new TootApiResult( response,url + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{

View File

@ -27,6 +27,10 @@ public class TootApiResult {
public TootApiResult( String error ){
this.error = error;
}
public TootApiResult( Response response, String error ){
this.response =response;
this.error = error;
}
TootApiResult( Response response, JSONObject token_info, String json, JSONObject object ){
this.token_info = token_info;
@ -53,6 +57,8 @@ public class TootApiResult {
private static final Pattern reLinkURL = Pattern.compile("<([^>]+)>;\\s*rel=\"([^\"]+)\"");
private void parseLinkHeader(LogCategory log,Response response, JSONArray array){
// Link: <https://mastodon.juggler.jp/api/v1/timelines/home?limit=XX&max_id=405228>; rel="next",
// <https://mastodon.juggler.jp/api/v1/timelines/home?limit=XX&since_id=436946>; rel="prev"

View File

@ -186,8 +186,9 @@
<string name="mention2">Mention</string>
<string name="menu">Menu</string>
<string name="minimum_column_width">Largeur minimum des colonnes \n(défaut=300(dp), redémarrage nécessaire)</string>
<string name="missing_storage_permission">Permission d\'écrire sur la SDCard manquante.</string>
<string name="more">Plus</string>
<string name="missing_permission_to_access_media">Missing app permission to access media.</string>
<string name="more">Plus</string>
<string name="mute">Désactiver le son</string>
<string name="mute_app_desc">Faire glisser pour supprimer. Vous devez rafraîchir pour voir les changements effectués.</string>
<string name="mute_app_of">Interdire l\'application \"%1$s\""</string>
@ -472,11 +473,14 @@
<string name="short_acct_local_user">Short acct for local user (app restart required)</string>
<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="error_notification_summary">If it is destructed 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="video_pick">Pick video</string>
<string name="video_capture">Capture video</string>
<string name="already_followed">Already followed.</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>-->

View File

@ -422,7 +422,8 @@
<!---->
<string name="attachment_uploading">添付メディアのアップロード中です…</string>
<!---->
<string name="missing_storage_permission">ストレージ書き込み権限がありません</string>
<string name="missing_permission_to_access_media">メディアアクセスに必要なアプリ権限がありません</string>
<!---->
<string name="send_message_from_another_account">別アカウントでメッセージを送る</string>
<!---->
@ -754,14 +755,17 @@
<string name="profile_pin">プロフィールに固定表示</string>
<string name="profile_unpin">プロフィールの固定表示を解除</string>
<string name="profile_pin_progress">固定表示の変更中…</string>
<string name="length_warning">\"%1$s\"が長すぎます(%2$d/%3$d文字).\n標準的なインスタンスではエラーとなりますが、いくつかのインスタンスでは許容されるかも。\nよろしいですか?</string>
<string name="length_warning">\"%1$s\"が長すぎます(%2$d/%3$d文字).\n標準的なタンスではエラーとなりますが、一部のタンスでは通るかも。\nよろしいですか?</string>
<string name="avatar_icon_size">アバターアイコンサイズ(単位:dp. デフォルト:48. アプリ再起動が必要)</string>
<string name="short_acct_local_user">ローカルユーザのAcct表記を短くする(アプリ再起動が必要)</string>
<string name="loading_notification_title">通知の取得中…</string>
<string name="error_notification_title">サーバータイムアウト</string>
<string name="error_notification_summary">もし死んだインスタンスなら、そのサーバ上のアカウントを除去してください</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>
<string name="video_pick">Pick video</string>
<string name="video_capture">Capture video</string>
<string name="already_followed">既にフォロー済みです</string>
</resources>

View File

@ -212,7 +212,7 @@
<string name="long_side_pixel">Resize to %1$d pixels</string>
<string name="image_pick">Pick an image</string>
<string name="image_capture">Take a picture</string>
<string name="missing_storage_permission">Permission to write storage is missing.</string>
<string name="missing_permission_to_access_media">Missing app permission to access media.</string>
<string name="attachment_uploading">Media attachment is uploading…</string>
<string name="attachment_uploaded">Media attachment upload completed.</string>
<string name="actions_for_status">Actions for this toot</string>
@ -466,10 +466,13 @@
<string name="short_acct_local_user">Short acct for local user (app restart required)</string>
<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="error_notification_summary">If it is destructed 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="video_pick">Pick video</string>
<string name="video_capture">Capture video</string>
<string name="already_followed">Already followed.</string>
</resources>