diff --git a/app/build.gradle b/app/build.gradle index 718f5218..00fb71ca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "jp.juggler.subwaytooter" minSdkVersion 21 targetSdkVersion 26 - versionCode 135 - versionName "1.3.5" + versionCode 137 + versionName "1.3.7" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.java b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.java index 2ceb7016..9e2607fb 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.java @@ -424,11 +424,11 @@ public class ActAccountSetting extends AppCompatActivity break; case R.id.btnDisplayName: - sendDisplayName(); + sendDisplayName(false); break; case R.id.btnNote: - sendNote(); + sendNote(false); break; case R.id.btnNotificationStyleEdit: @@ -576,6 +576,7 @@ public class ActAccountSetting extends AppCompatActivity /////////////////////////////////////////////////// private void performAccessToken(){ + //noinspection deprecation final ProgressDialog progress = new ProgressDialog( ActAccountSetting.this ); final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() { @@ -692,6 +693,7 @@ public class ActAccountSetting extends AppCompatActivity void loadProfile(){ // サーバから情報をロードする + //noinspection deprecation final ProgressDialog progress = new ProgressDialog( this ); final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() { @@ -772,6 +774,8 @@ public class ActAccountSetting extends AppCompatActivity } void updateCredential( final String form_data ){ + + //noinspection deprecation final ProgressDialog progress = new ProgressDialog( this ); final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() { @@ -814,7 +818,7 @@ public class ActAccountSetting extends AppCompatActivity progress.dismiss(); }catch( Throwable ignored ){ } - loadProfile(); + if( result == null ){ // cancelled. }else if( data != null ){ @@ -835,12 +839,55 @@ public class ActAccountSetting extends AppCompatActivity progress.show(); } - private void sendDisplayName(){ - updateCredential( "display_name=" + Uri.encode( etDisplayName.getText().toString() ) ); + static final int max_length_display_name = 30; + static final int max_length_note = 160; + + private void sendDisplayName(boolean bConfirmed){ + String sv = etDisplayName.getText().toString(); + if( !bConfirmed ){ + int length = sv.codePointCount( 0,sv.length() ); + if( length > max_length_display_name ){ + new AlertDialog.Builder( this ) + .setMessage( getString(R.string.length_warning + ,getString(R.string.display_name) + ,length,max_length_display_name + )) + .setNegativeButton( R.string.cancel ,null) + .setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() { + @Override public void onClick( DialogInterface dialogInterface, int i ){ + sendDisplayName(true); + } + } ) + .setCancelable( true ) + .show(); + return; + } + } + updateCredential( "display_name=" + Uri.encode(sv ) ); } - private void sendNote(){ - updateCredential( "note=" + Uri.encode( etNote.getText().toString() ) ); + private void sendNote(boolean bConfirmed){ + String sv = etNote.getText().toString(); + if( !bConfirmed ){ + int length = sv.codePointCount( 0,sv.length() ); + if( length > max_length_note ){ + new AlertDialog.Builder( this ) + .setMessage( getString(R.string.length_warning + ,getString(R.string.note) + ,length,max_length_note + )) + .setNegativeButton( R.string.cancel ,null) + .setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() { + @Override public void onClick( DialogInterface dialogInterface, int i ){ + sendNote(true); + } + } ) + .setCancelable( true ) + .show(); + return; + } + } + updateCredential( "note=" + Uri.encode(sv ) ); } private static final int PERMISSION_REQUEST_AVATAR = 1; diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java index b8e0cc72..183b1a4d 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java @@ -104,6 +104,7 @@ public class ActAppSetting extends AppCompatActivity Switch swMentionFullAcct; Switch swRelativeTimestamp; Switch swDontUseActionButtonWithQuickTootBar; + Switch swShortAcctLocalUser; Spinner spBackButtonAction; Spinner spUITheme; @@ -148,6 +149,7 @@ public class ActAppSetting extends AppCompatActivity EditText etAcctFontSize; TextView tvTimelineFontSize; TextView tvAcctFontSize; + EditText etAvatarIconSize; static final float default_timeline_font_size = 14f; static final float default_acct_font_size = 12f; @@ -217,6 +219,9 @@ public class ActAppSetting extends AppCompatActivity swDontUseActionButtonWithQuickTootBar = findViewById( R.id.swDontUseActionButtonWithQuickTootBar ); swDontUseActionButtonWithQuickTootBar.setOnCheckedChangeListener( this ); + swShortAcctLocalUser = findViewById( R.id.swShortAcctLocalUser ); + swShortAcctLocalUser.setOnCheckedChangeListener( this ); + cbNotificationSound = findViewById( R.id.cbNotificationSound ); cbNotificationVibration = findViewById( R.id.cbNotificationVibration ); cbNotificationLED = findViewById( R.id.cbNotificationLED ); @@ -344,6 +349,8 @@ public class ActAppSetting extends AppCompatActivity etAcctFontSize = findViewById( R.id.etAcctFontSize ); etAcctFontSize.addTextChangedListener( new SizeCheckTextWatcher( tvAcctFontSize, etAcctFontSize, default_acct_font_size ) ); + etAvatarIconSize = findViewById( R.id.etAvatarIconSize ); + tvTimelineFontUrl = findViewById( R.id.tvTimelineFontUrl ); tvTimelineFontBoldUrl = findViewById( R.id.tvTimelineFontBoldUrl ); @@ -372,6 +379,7 @@ public class ActAppSetting extends AppCompatActivity swMentionFullAcct.setChecked( pref.getBoolean( Pref.KEY_MENTION_FULL_ACCT, false ) ); swRelativeTimestamp.setChecked( pref.getBoolean( Pref.KEY_RELATIVE_TIMESTAMP, false ) ); swDontUseActionButtonWithQuickTootBar.setChecked( pref.getBoolean( Pref.KEY_DONT_USE_ACTION_BUTTON, false ) ); + swShortAcctLocalUser.setChecked( pref.getBoolean( Pref.KEY_SHORT_ACCT_LOCAL_USER, false ) ); // Switch with default true swDisableFastScroller.setChecked( pref.getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, true ) ); @@ -401,6 +409,7 @@ public class ActAppSetting extends AppCompatActivity etClientName.setText( pref.getString( Pref.KEY_CLIENT_NAME, "" ) ); etQuoteNameFormat.setText( pref.getString( Pref.KEY_QUOTE_NAME_FORMAT, "" ) ); etAutoCWLines.setText( pref.getString( Pref.KEY_AUTO_CW_LINES, "0" ) ); + etAvatarIconSize.setText( pref.getString( Pref.KEY_AVATAR_ICON_SIZE, "48" ) ); etTimelineFontSize.setText( formatFontSize( pref.getFloat( Pref.KEY_TIMELINE_FONT_SIZE, Float.NaN ) ) ); etAcctFontSize.setText( formatFontSize( pref.getFloat( Pref.KEY_ACCT_FONT_SIZE, Float.NaN ) ) ); @@ -441,6 +450,9 @@ public class ActAppSetting extends AppCompatActivity .putBoolean( Pref.KEY_MENTION_FULL_ACCT, swMentionFullAcct.isChecked() ) .putBoolean( Pref.KEY_RELATIVE_TIMESTAMP, swRelativeTimestamp.isChecked() ) .putBoolean( Pref.KEY_DONT_USE_ACTION_BUTTON, swDontUseActionButtonWithQuickTootBar.isChecked() ) + .putBoolean( Pref.KEY_SHORT_ACCT_LOCAL_USER, swShortAcctLocalUser.isChecked() ) + + .putBoolean( Pref.KEY_NOTIFICATION_SOUND, cbNotificationSound.isChecked() ) .putBoolean( Pref.KEY_NOTIFICATION_VIBRATION, cbNotificationVibration.isChecked() ) @@ -467,6 +479,8 @@ public class ActAppSetting extends AppCompatActivity .putString( Pref.KEY_CLIENT_NAME, etClientName.getText().toString().trim() ) .putString( Pref.KEY_QUOTE_NAME_FORMAT, etQuoteNameFormat.getText().toString() ) // not trimmed .putString( Pref.KEY_AUTO_CW_LINES, etAutoCWLines.getText().toString() ) // not trimmed + .putString( Pref.KEY_AVATAR_ICON_SIZE, etAvatarIconSize.getText().toString().trim() ) + .putFloat( Pref.KEY_TIMELINE_FONT_SIZE, parseFontSize( etTimelineFontSize.getText().toString().trim() ) ) .putFloat( Pref.KEY_ACCT_FONT_SIZE, parseFontSize( etAcctFontSize.getText().toString().trim() ) ) diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java index 0535d95c..028b041a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java @@ -770,6 +770,8 @@ public class ActMain extends AppCompatActivity Typeface timeline_font_bold; boolean dont_crop_media_thumbnail; + boolean mShortAcctLocalUser; + int mAvatarIconSize; View llQuickTootBar; MyEditText etQuickToot; @@ -804,7 +806,24 @@ public class ActMain extends AppCompatActivity log.trace( ex ); } } - + + mShortAcctLocalUser = pref.getBoolean( Pref.KEY_SHORT_ACCT_LOCAL_USER ,false); + + { + float icon_size_dp = 48f; + try{ + sv = pref.getString( Pref.KEY_AVATAR_ICON_SIZE, "48" ); + float fv = Float.parseFloat( sv ); + if( Float.isNaN( fv ) || Float.isInfinite( fv ) || fv < 1f ){ + // error or bad range + }else{ + icon_size_dp = fv; + } + }catch(Throwable ex){ + log.trace( ex ); + } + mAvatarIconSize = (int)(0.5f + icon_size_dp * density ); + } llEmpty = findViewById( R.id.llEmpty ); @@ -2669,6 +2688,7 @@ public class ActMain extends AppCompatActivity } static final Pattern reUriOStatusToot = Pattern.compile( "tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE ); + // static final Pattern reUriActivityPubToot = Pattern.compile( "tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE ); public void openStatusOtherInstance( int pos, @NonNull SavedAccount access_info, @NonNull TootStatusLike status ){ if( status.account == null ){ @@ -2699,6 +2719,13 @@ public class ActMain extends AppCompatActivity if( m.find() ){ status_id_original = Long.parseLong( m.group( 2 ), 10 ); // host_original = m.group( 1 ); + }else{ + // TODO ActivityPub対応 +// m = reUriOStatusToot.matcher( ts.uri ); +// if( m.find() ){ +// status_id_original = Long.parseLong( m.group( 2 ), 10 ); +// // host_original = m.group( 1 ); +// } } }catch( Throwable ex ){ log.e( ex, "openStatusOtherInstance: cant parse tag: %s", ts.uri ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPost.java b/app/src/main/java/jp/juggler/subwaytooter/ActPost.java index 16c023ee..1d765b4f 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActPost.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActPost.java @@ -651,6 +651,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, @Override public void onCheckedChanged( CompoundButton buttonView, boolean isChecked ){ showEnquete(); + updateTextCount(); } } ); @@ -665,20 +666,10 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, } } ); - etContentWarning.addTextChangedListener( new TextWatcher() { - @Override - public void beforeTextChanged( CharSequence s, int start, int count, int after ){ - - } - - @Override public void onTextChanged( CharSequence s, int start, int before, int count ){ - - } - - @Override public void afterTextChanged( Editable s ){ - updateTextCount(); - } - } ); + etContentWarning.addTextChangedListener( text_watcher); + for(MyEditText et : list_etChoice ){ + et.addTextChangedListener( text_watcher); + } scrollView.getViewTreeObserver().addOnScrollChangedListener( scroll_listener ); @@ -686,6 +677,20 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, v.setOnClickListener( this ); } + final TextWatcher text_watcher = new TextWatcher() { + @Override public void beforeTextChanged( CharSequence charSequence, int i, int i1, int i2 ){ + + } + + @Override public void onTextChanged( CharSequence charSequence, int i, int i1, int i2 ){ + + } + + @Override public void afterTextChanged( Editable editable ){ + updateTextCount(); + } + }; + final ViewTreeObserver.OnScrollChangedListener scroll_listener = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged(){ post_helper.onScrollChanged(); @@ -694,12 +699,26 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, }; private void updateTextCount(){ - String s = etContent.getText().toString(); - int count_content = s.codePointCount( 0, s.length() ); - s = cbContentWarning.isChecked() ? etContentWarning.getText().toString() : ""; - int count_spoiler = s.codePointCount( 0, s.length() ); + int length = 0; - int remain = 500 - count_content - count_spoiler; + String s = etContent.getText().toString(); + length += s.codePointCount( 0, s.length() ); + + s = cbContentWarning.isChecked() ? etContentWarning.getText().toString() : ""; + length += s.codePointCount( 0, s.length() ); + + int max; + if( !cbEnquete.isChecked() ){ + max = 500; + }else{ + max = 350; + for( MyEditText et : list_etChoice){ + s = et.getText().toString(); + length += s.codePointCount( 0, s.length() ); + } + } + + int remain = max - length; tvCharCount.setText( Integer.toString( remain ) ); int color = Styler.getAttributeColor( this, remain < 0 ? R.attr.colorRegexFilterError : android.R.attr.textColorPrimary ); tvCharCount.setTextColor( color ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/App1.java b/app/src/main/java/jp/juggler/subwaytooter/App1.java index d4169f20..62e66f4c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/App1.java +++ b/app/src/main/java/jp/juggler/subwaytooter/App1.java @@ -285,8 +285,8 @@ public class App1 extends Application { OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout( 30, TimeUnit.SECONDS ) - .readTimeout( 30, TimeUnit.SECONDS ) - .writeTimeout( 30, TimeUnit.SECONDS ) + .readTimeout( 60, TimeUnit.SECONDS ) + .writeTimeout( 60, TimeUnit.SECONDS ) .pingInterval( 10, TimeUnit.SECONDS ) .connectionSpecs( spec_list ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java index 8491cc1d..94b1c763 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java +++ b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java @@ -317,6 +317,7 @@ public class AppDataExporter { case Pref.KEY_MENTION_FULL_ACCT: case Pref.KEY_RELATIVE_TIMESTAMP: case Pref.KEY_DONT_USE_ACTION_BUTTON: + case Pref.KEY_SHORT_ACCT_LOCAL_USER: boolean bv = reader.nextBoolean(); e.putBoolean( k, bv ); break; @@ -352,6 +353,7 @@ public class AppDataExporter { case Pref.KEY_MASTODON_SEARCH_PORTAL_USER_TOKEN: case Pref.KEY_QUOTE_NAME_FORMAT: case Pref.KEY_AUTO_CW_LINES: + case Pref.KEY_AVATAR_ICON_SIZE: String sv = reader.nextString(); e.putString( k, sv ); break; diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java index 6267d19a..c07db92d 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java @@ -218,6 +218,10 @@ class Column implements StreamReader.Callback { boolean search_resolve; String instance_uri; + + // 「インスタンス情報」カラムに表示するインスタンス情報 + // (SavedAccount中のインスタンス情報とは異なるので注意) + TootInstance instance_information; ScrollPosition scroll_save; @@ -1185,14 +1189,15 @@ class Column implements StreamReader.Callback { return result; } + TootInstance instance_tmp; + 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 ){ - TootInstance instance = TootInstance.parse( result.object ); - if( instance != null ){ - access_info.refInstance.set( instance ); - } + instance_tmp = TootInstance.parse( result.object ); + } return result; } @@ -1202,7 +1207,7 @@ class Column implements StreamReader.Callback { TootApiResult result = client.request( path_base ); if( result != null && result.array != null ){ // - TootStatus.List src = TootStatus.parseList( context, access_info, result.array ); + 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); @@ -1248,7 +1253,7 @@ class Column implements StreamReader.Callback { // break; // } // -// src = TootStatus.parseList( context, access_info, result2.array ); +// src = TootStatus.parseList( context, access_info, result2.array ,true); // for(TootStatus status : src ){ // log.d("pinned: %s %s",status.id, status.decoded_content); // } @@ -1459,12 +1464,16 @@ class Column implements StreamReader.Callback { default: case TAB_STATUS: - TootInstance instance = access_info.refInstance.get(); + TootInstance instance = access_info.getInstance(); if( access_info.isPseudo() || instance == null ){ TootApiResult r2 = getInstanceInformation( client ,null ); - instance = access_info.refInstance.get(); + 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"; @@ -1612,8 +1621,13 @@ class Column implements StreamReader.Callback { } return result; - case TYPE_INSTANCE_INFORMATION: - return getInstanceInformation( client ,instance_uri); + case TYPE_INSTANCE_INFORMATION:{ + result = getInstanceInformation( client, instance_uri ); + if( instance_tmp != null ){ + instance_information = instance_tmp; + } + return result; + } } }finally{ try{ diff --git a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderInstance.java b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderInstance.java index cc966c4c..2aaa12f1 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderInstance.java +++ b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderInstance.java @@ -75,7 +75,7 @@ class HeaderViewHolderInstance extends HeaderViewHolderBase implements View.OnCl @Override void bindData( Column column ){ this.column = column; - this.instance = column.access_info.refInstance.get(); + this.instance = column.instance_information; if( instance == null ){ btnInstance.setText( "?" ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderSearchDesc.java b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderSearchDesc.java index 2e9876ad..faaf3b69 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderSearchDesc.java +++ b/app/src/main/java/jp/juggler/subwaytooter/HeaderViewHolderSearchDesc.java @@ -18,21 +18,24 @@ class HeaderViewHolderSearchDesc extends HeaderViewHolderBase { , arg_activity.getLayoutInflater().inflate( R.layout.lv_header_search_desc, parent, false ) ); - if( activity.timeline_font != null ){ - Utils.scanView( viewRoot, new Utils.ScanViewCallback() { - @Override public void onScanView( View v ){ - try{ - if( v instanceof Button ){ - // ボタンは太字なので触らない - }else if( v instanceof TextView ){ + Utils.scanView( viewRoot, new Utils.ScanViewCallback() { + @Override public void onScanView( View v ){ + try{ + if( v instanceof Button ){ + // ボタンは太字なので触らない + }else if( v instanceof TextView ){ + if( activity.timeline_font != null ){ ( (TextView) v ).setTypeface( activity.timeline_font ); } - }catch( Throwable ex ){ - log.trace( ex ); + if( ! Float.isNaN( activity.timeline_font_size_sp ) ){ + ( (TextView) v ).setTextSize( activity.timeline_font_size_sp ); + } } + }catch( Throwable ex ){ + log.trace( ex ); } - } ); - } + } + } ); CharSequence sv = HTMLDecoder.decodeHTML( activity, access_info, html, false, true, null ); diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java index c3c114ed..ec872544 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.java @@ -243,6 +243,12 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { tvAcct.setTextSize( activity.acct_font_size_sp ); tvTime.setTextSize( activity.acct_font_size_sp ); } + + ivBoosted.getLayoutParams().width = + ivFollow.getLayoutParams().width = + ivThumbnail.getLayoutParams().width = + ivThumbnail.getLayoutParams().height = activity.mAvatarIconSize; + } void bind( Object item ){ @@ -379,7 +385,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { ivBoosted.setImageResource( Styler.getAttributeResourceId( activity, icon_attr_id ) ); tvBoostedTime.setText( TootStatus.formatTime( tvBoostedTime.getContext(), time, true ) ); tvBoosted.setText( text ); - setAcct( tvBoostedAcct, access_info.getFullAcct( who ) ); + setAcct( tvBoostedAcct, access_info.getFullAcct( who ) ,who.acct); } private void showFollow( @NonNull TootAccount who ){ @@ -387,7 +393,7 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { llFollow.setVisibility( View.VISIBLE ); ivFollow.setImageUrl( activity.pref, 16f, access_info.supplyBaseUrl( who.avatar_static ) ); tvFollowerName.setText( who.decoded_display_name ); - setAcct( tvFollowerAcct, access_info.getFullAcct( who ) ); + setAcct( tvFollowerAcct, access_info.getFullAcct( who ) ,who.acct ); UserRelation relation = UserRelation.load( access_info.db_id, who.id ); Styler.setFollowIcon( activity, btnFollow, ivFollowedBy, relation ); @@ -399,8 +405,9 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { showStatusTime( activity, status ); - account_thumbnail = status.account; - setAcct( tvAcct, access_info.getFullAcct( status.account ) ); + TootAccount who = account_thumbnail = status.account; + + setAcct( tvAcct, access_info.getFullAcct( who ) ,who == null ? "?" : who.acct ); if( status.account == null ){ tvName.setText( "?" ); @@ -570,9 +577,9 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener { tvTime.setText( sb ); } - private void setAcct( TextView tv, String acct ){ - AcctColor ac = AcctColor.load( acct ); - tv.setText( AcctColor.hasNickname( ac ) ? ac.nickname : acct ); + private void setAcct( @NonNull TextView tv, @NonNull String acctLong,@NonNull String acctShort ){ + AcctColor ac = AcctColor.load( acctLong ); + tv.setText( AcctColor.hasNickname( ac ) ? ac.nickname : activity.mShortAcctLocalUser ? "@"+acctShort : acctLong ); tv.setTextColor( AcctColor.hasColorForeground( ac ) ? ac.color_fg : this.acct_color ); if( AcctColor.hasColorBackground( ac ) ){ diff --git a/app/src/main/java/jp/juggler/subwaytooter/Pref.java b/app/src/main/java/jp/juggler/subwaytooter/Pref.java index abde19f7..29136714 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Pref.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Pref.java @@ -84,6 +84,9 @@ public class Pref { public static final String KEY_AUTO_CW_LINES = "auto_cw_lines"; + public static final String KEY_SHORT_ACCT_LOCAL_USER = "short_acct_local_user"; + + public static final String KEY_AVATAR_ICON_SIZE = "avatar_icon_size"; // 項目を追加したらAppDataExporter#importPref のswitch文も更新すること } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.java b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.java index 45c14b60..463d72f4 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.java @@ -1,13 +1,13 @@ package jp.juggler.subwaytooter.api.entity; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import org.json.JSONObject; - -import jp.juggler.subwaytooter.util.LogCategory; -import jp.juggler.subwaytooter.util.Utils; -import jp.juggler.subwaytooter.util.VersionString; + + import android.support.annotation.NonNull; + import android.support.annotation.Nullable; + + import org.json.JSONObject; + + import jp.juggler.subwaytooter.util.LogCategory; + import jp.juggler.subwaytooter.util.Utils; + import jp.juggler.subwaytooter.util.VersionString; @SuppressWarnings("WeakerAccess") public class TootInstance { @@ -27,8 +27,12 @@ public class TootInstance { public String version; + // バージョンの内部表現 public VersionString decoded_version; + // いつ取得したか + public long time_parse; + @Nullable public static TootInstance parse( JSONObject src ){ if( src == null ) return null; @@ -40,6 +44,7 @@ public class TootInstance { dst.email = Utils.optStringX( src, "email" ); dst.version = Utils.optStringX( src, "version" ); dst.decoded_version = new VersionString( dst.version ); + dst.time_parse = System.currentTimeMillis(); return dst; }catch( Throwable ex ){ log.trace( ex ); @@ -48,11 +53,9 @@ public class TootInstance { } } - - public boolean isEnoughVersion( @NonNull VersionString check ){ if( decoded_version.isEmpty() || check.isEmpty() ) return false; - int i = VersionString.compare( decoded_version ,check ); + int i = VersionString.compare( decoded_version, check ); return i >= 0; } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.java b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.java index 339e9f06..35c31c1b 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.java +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.java @@ -87,6 +87,14 @@ public class TootStatus extends TootStatusLike { @Nullable public static TootStatus parse( @NonNull Context context, @NonNull SavedAccount access_info, JSONObject src ){ + return parse( context,access_info,src,false); + } + + @Nullable + public static TootStatus parse( @NonNull Context context, @NonNull SavedAccount access_info, JSONObject src ,boolean bPinned){ + /* + bPinned 引数がtrueになるのはプロフィールカラムからpinned TL を読んだ時だけである + */ if( src == null ) return null; // log.d( "parse: %s", src.toString() ); @@ -111,7 +119,8 @@ public class TootStatus extends TootStatusLike { status.in_reply_to_id = Utils.optStringX( src, "in_reply_to_id" ); // null status.in_reply_to_account_id = Utils.optStringX( src, "in_reply_to_account_id" ); // null - status.reblog = TootStatus.parse( context, access_info, src.optJSONObject( "reblog" ) ); + status.reblog = TootStatus.parse( context, access_info, src.optJSONObject( "reblog" ) ,false ); + /* Pinned TL を取得した時にreblogが登場することはないので、reblogをパースするときのbPinnedはfalseでよい */ status.content = Utils.optStringX( src, "content" ); status.created_at = Utils.optStringX( src, "created_at" ); // "2017-04-16T09:37:14.000Z" status.reblogs_count = src.optLong( "reblogs_count" ); @@ -125,7 +134,7 @@ public class TootStatus extends TootStatusLike { status.tags = TootTag.parseList( src.optJSONArray( "tags" ) ); status.application = TootApplication.parse( src.optJSONObject( "application" ) ); // null - status.pinned = src.optBoolean( "pinned" ); + status.pinned = bPinned || src.optBoolean( "pinned" ); status.setSpoilerText( context, Utils.optStringX( src, "spoiler_text" ) ); @@ -137,7 +146,6 @@ public class TootStatus extends TootStatusLike { // status.decoded_tags = HTMLDecoder.decodeTags( account,status.tags ); status.decoded_mentions = HTMLDecoder.decodeMentions( access_info, status.mentions ); - status.enquete = NicoEnquete.parse( context,access_info , status.media_attachments , Utils.optStringX( src, "enquete"),status.id,status.time_created_at ); return status; @@ -150,6 +158,11 @@ public class TootStatus extends TootStatusLike { @NonNull public static List parseList( @NonNull Context context, @NonNull SavedAccount access_info, JSONArray array ){ + return parseList( context,access_info,array,false ); + } + + @NonNull + public static List parseList( @NonNull Context context, @NonNull SavedAccount access_info, JSONArray array ,boolean bPinned){ List result = new List(); if( array != null ){ int array_size = array.length(); @@ -157,7 +170,7 @@ public class TootStatus extends TootStatusLike { for( int i = 0 ; i < array_size ; ++ i ){ JSONObject src = array.optJSONObject( i ); if( src == null ) continue; - TootStatus item = parse( context, access_info, src ); + TootStatus item = parse( context, access_info, src ,bPinned ); if( item != null ) result.add( item ); } } 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 666faf14..40c5a917 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java +++ b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.java @@ -84,8 +84,20 @@ public class SavedAccount extends TootAccount implements LinkClickContext { public long register_time; + private final AtomicReference refInstance = new AtomicReference<>( null ); + private static final long INSTANCE_INFORMATION_EXPIRE = 60000L * 5; + // DBには保存しない - public final AtomicReference refInstance = new AtomicReference<>( null ); + public @Nullable TootInstance getInstance(){ + TootInstance instance = refInstance.get(); + if( instance != null && System.currentTimeMillis() - instance.time_parse > INSTANCE_INFORMATION_EXPIRE ){ + return null; + } + return instance; + } + public void setInstance(@NonNull TootInstance instance){ + if( instance != null ) refInstance.set(instance); + } // アプリデータのインポート時に呼ばれる diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.java b/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.java index d8533924..2dd16936 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.java +++ b/app/src/main/java/jp/juggler/subwaytooter/util/VersionString.java @@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.util; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import java.math.BigInteger; import java.util.ArrayList; @@ -9,17 +10,25 @@ import java.util.ArrayList; public class VersionString { // static final LogCategory log = new LogCategory( "VersionString" ); - private final String src; - - private final ArrayList node_list = new ArrayList<>( ); - + @NonNull private final String src; + + @Override public String toString(){ + return src; + } + + private final ArrayList< Object > node_list = new ArrayList<>(); + + public boolean isEmpty(){ + return node_list.isEmpty(); + } + private static boolean isDelimiter( char c ){ return c == '.' || c == ' '; } public VersionString( @Nullable String src ){ - this.src = src; - if( src == null ) return; + this.src = src == null ? "" : src; + if( TextUtils.isEmpty( src ) ) return; int end = src.length(); int next = 0; while( next < end ){ @@ -49,14 +58,10 @@ public class VersionString { } } - public boolean isEmpty(){ - return node_list.isEmpty(); - } - // return -1 if ab , return 0 if a==b public static int compare( @NonNull VersionString a, @NonNull VersionString b ){ - for( int idx =0 ;; ++idx){ + for( int idx = 0 ; ; ++ idx ){ Object ao = ( idx >= a.node_list.size() ? null : a.node_list.get( idx ) ); Object bo = ( idx >= b.node_list.size() ? null : b.node_list.get( idx ) ); if( ao == null ){ diff --git a/app/src/main/res/layout/act_app_setting.xml b/app/src/main/res/layout/act_app_setting.xml index bcdb6f99..0f41eb6d 100644 --- a/app/src/main/res/layout/act_app_setting.xml +++ b/app/src/main/res/layout/act_app_setting.xml @@ -591,6 +591,23 @@ + + + + + + + + + + + + + + + + + + + + Pin on profile Unpin from profile Changing pinned status… + \"%1$s\" is too long (%2$d/%3$d characters).\nIt is not acceptable in the standard instance, but it may be acceptable in some instances.\nAre you sure? + Avatar icon size (unit:dp. default:48. app restart required) + Short acct for local user (app restart required) - + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 66d35bd2..0cf3835b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -752,4 +752,8 @@ プロフィールに固定表示 プロフィールの固定表示を解除 固定表示の変更中… + \"%1$s\"が長すぎます(%2$d/%3$d文字).\n標準的なインスタンスではエラーとなりますが、いくつかのインスタンスでは許容されるかも。\nよろしいですか? + アバターアイコンサイズ(単位:dp. デフォルト:48. アプリ再起動が必要) + ローカルユーザのAcct表記を短くする(アプリ再起動が必要) + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index daa5def7..bdcc474a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -459,5 +459,8 @@ Pin on profile Unpin from profile Changing pinned status… + \"%1$s\" is too long (%2$d/%3$d characters).\nIt is not acceptable in the standard instance, but it may be acceptable in some instances.\nAre you sure? + Avatar icon size (unit:dp. default:48. app restart required) + Short acct for local user (app restart required)