From 969654341f061c593c1a7251602ff0dca609f95b Mon Sep 17 00:00:00 2001 From: tateisu Date: Sat, 27 May 2017 07:36:08 +0900 Subject: [PATCH] v0.7.3 --- .idea/dictionaries/tateisu.xml | 2 + app/build.gradle | 4 +- .../juggler/subwaytooter/ActAppSetting.java | 49 ++++- .../java/jp/juggler/subwaytooter/ActMain.java | 58 +++++- .../java/jp/juggler/subwaytooter/ActPost.java | 174 ++++++++++++++++-- .../java/jp/juggler/subwaytooter/App1.java | 6 +- .../juggler/subwaytooter/AppDataExporter.java | 1 + .../java/jp/juggler/subwaytooter/Column.java | 50 ++++- .../java/jp/juggler/subwaytooter/Pref.java | 1 + .../subwaytooter/dialog/LoginForm.java | 2 +- .../jp/juggler/subwaytooter/table/TagSet.java | 144 +++++++++++++++ .../view/ColumnStripLinearLayout.java | 83 +++++++++ app/src/main/res/drawable-hdpi/ic_plugin.png | Bin 0 -> 613 bytes .../main/res/drawable-hdpi/ic_plugin_dark.png | Bin 0 -> 538 bytes app/src/main/res/drawable-mdpi/ic_plugin.png | Bin 0 -> 429 bytes .../main/res/drawable-mdpi/ic_plugin_dark.png | Bin 0 -> 355 bytes app/src/main/res/drawable-xhdpi/ic_plugin.png | Bin 0 -> 714 bytes .../res/drawable-xhdpi/ic_plugin_dark.png | Bin 0 -> 619 bytes .../main/res/drawable-xxhdpi/ic_plugin.png | Bin 0 -> 1192 bytes .../res/drawable-xxhdpi/ic_plugin_dark.png | Bin 0 -> 1039 bytes app/src/main/res/layout/act_app_setting.xml | 167 ++++++++++------- app/src/main/res/layout/act_main.xml | 2 +- app/src/main/res/layout/act_post.xml | 10 + app/src/main/res/values-fr/strings.xml | 6 +- app/src/main/res/values-ja/strings.xml | 6 +- app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/strings.xml | 8 +- app/src/main/res/values/styles.xml | 3 +- 28 files changed, 672 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/jp/juggler/subwaytooter/table/TagSet.java create mode 100644 app/src/main/java/jp/juggler/subwaytooter/view/ColumnStripLinearLayout.java create mode 100644 app/src/main/res/drawable-hdpi/ic_plugin.png create mode 100644 app/src/main/res/drawable-hdpi/ic_plugin_dark.png create mode 100644 app/src/main/res/drawable-mdpi/ic_plugin.png create mode 100644 app/src/main/res/drawable-mdpi/ic_plugin_dark.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_plugin.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_plugin_dark.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_plugin.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_plugin_dark.png diff --git a/.idea/dictionaries/tateisu.xml b/.idea/dictionaries/tateisu.xml index fda9ce3d..bb8296a0 100644 --- a/.idea/dictionaries/tateisu.xml +++ b/.idea/dictionaries/tateisu.xml @@ -1,6 +1,7 @@ + adamrocker dont emoji emojione @@ -18,6 +19,7 @@ reblogged reblogs sephiroth + simeji styler subwaytooter swipy diff --git a/app/build.gradle b/app/build.gradle index afd37f5c..b62ae566 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "jp.juggler.subwaytooter" minSdkVersion 21 targetSdkVersion 25 - versionCode 72 - versionName "0.7.2" + versionCode 73 + versionName "0.7.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java index 82e7a433..cf435d6c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActAppSetting.java @@ -38,7 +38,6 @@ import org.apache.commons.io.output.FileWriterWithEncoding; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; -import java.lang.ref.WeakReference; import jp.juggler.subwaytooter.table.SavedAccount; import jp.juggler.subwaytooter.util.LogCategory; @@ -101,16 +100,17 @@ public class ActAppSetting extends AppCompatActivity int footer_button_fg_color; int footer_tab_bg_color; int footer_tab_divider_color; + int footer_tab_indicator_color; ImageView ivFooterToot; ImageView ivFooterMenu; View llFooterBG; View vFooterDivider1; View vFooterDivider2; + View vIndicator; EditText etColumnWidth; EditText etMediaThumbHeight; - TextView tvTimelineFontUrl; String timeline_font; @@ -228,6 +228,9 @@ public class ActAppSetting extends AppCompatActivity findViewById( R.id.btnTabBackgroundColorReset ).setOnClickListener( this ); findViewById( R.id.btnTabDividerColorEdit ).setOnClickListener( this ); findViewById( R.id.btnTabDividerColorReset ).setOnClickListener( this ); + findViewById( R.id.btnTabIndicatorColorEdit ).setOnClickListener( this ); + findViewById( R.id.btnTabIndicatorColorReset ).setOnClickListener( this ); + findViewById( R.id.btnTimelineFontEdit ).setOnClickListener( this ); findViewById( R.id.btnTimelineFontReset ).setOnClickListener( this ); findViewById( R.id.btnSettingExport ).setOnClickListener( this ); @@ -241,6 +244,7 @@ public class ActAppSetting extends AppCompatActivity llFooterBG = findViewById( R.id.llFooterBG ); vFooterDivider1 = findViewById( R.id.vFooterDivider1 ); vFooterDivider2 = findViewById( R.id.vFooterDivider2 ); + vIndicator = findViewById( R.id.vIndicator ); etColumnWidth = (EditText) findViewById( R.id.etColumnWidth ); etMediaThumbHeight = (EditText) findViewById( R.id.etMediaThumbHeight ); @@ -282,6 +286,7 @@ public class ActAppSetting extends AppCompatActivity footer_button_fg_color = pref.getInt( Pref.KEY_FOOTER_BUTTON_FG_COLOR, 0 ); footer_tab_bg_color = pref.getInt( Pref.KEY_FOOTER_TAB_BG_COLOR, 0 ); footer_tab_divider_color = pref.getInt( Pref.KEY_FOOTER_TAB_DIVIDER_COLOR, 0 ); + footer_tab_indicator_color = pref.getInt( Pref.KEY_FOOTER_TAB_INDICATOR_COLOR, 0 ); etColumnWidth.setText( pref.getString( Pref.KEY_COLUMN_WIDTH, "" ) ); etMediaThumbHeight.setText( pref.getString( Pref.KEY_MEDIA_THUMB_HEIGHT, "" ) ); @@ -324,7 +329,8 @@ public class ActAppSetting extends AppCompatActivity .putInt( Pref.KEY_FOOTER_BUTTON_FG_COLOR, footer_button_fg_color ) .putInt( Pref.KEY_FOOTER_TAB_BG_COLOR, footer_tab_bg_color ) .putInt( Pref.KEY_FOOTER_TAB_DIVIDER_COLOR, footer_tab_divider_color ) - + .putInt( Pref.KEY_FOOTER_TAB_INDICATOR_COLOR, footer_tab_indicator_color ) + .putString( Pref.KEY_TIMELINE_FONT, timeline_font ) .putString( Pref.KEY_COLUMN_WIDTH, etColumnWidth.getText().toString().trim() ) .putString( Pref.KEY_MEDIA_THUMB_HEIGHT, etMediaThumbHeight.getText().toString().trim() ) @@ -350,12 +356,13 @@ public class ActAppSetting extends AppCompatActivity static final int COLOR_DIALOG_ID_FOOTER_BUTTON_FG = 2; static final int COLOR_DIALOG_ID_FOOTER_TAB_BG = 3; static final int COLOR_DIALOG_ID_FOOTER_TAB_DIVIDER = 4; + static final int COLOR_DIALOG_ID_FOOTER_TAB_INDICATOR = 5; @Override public void onClick( View v ){ switch( v.getId() ){ case R.id.btnFooterBackgroundEdit: - openColorPicker( COLOR_DIALOG_ID_FOOTER_BUTTON_BG, footer_button_bg_color ); + openColorPicker( COLOR_DIALOG_ID_FOOTER_BUTTON_BG, footer_button_bg_color ,false); break; case R.id.btnFooterBackgroundReset: @@ -365,7 +372,7 @@ public class ActAppSetting extends AppCompatActivity break; case R.id.btnFooterForegroundColorEdit: - openColorPicker( COLOR_DIALOG_ID_FOOTER_BUTTON_FG, footer_button_fg_color ); + openColorPicker( COLOR_DIALOG_ID_FOOTER_BUTTON_FG, footer_button_fg_color ,false); break; case R.id.btnFooterForegroundColorReset: @@ -375,7 +382,7 @@ public class ActAppSetting extends AppCompatActivity break; case R.id.btnTabBackgroundColorEdit: - openColorPicker( COLOR_DIALOG_ID_FOOTER_TAB_BG, footer_tab_bg_color ); + openColorPicker( COLOR_DIALOG_ID_FOOTER_TAB_BG, footer_tab_bg_color ,false); break; case R.id.btnTabBackgroundColorReset: @@ -385,7 +392,7 @@ public class ActAppSetting extends AppCompatActivity break; case R.id.btnTabDividerColorEdit: - openColorPicker( COLOR_DIALOG_ID_FOOTER_TAB_DIVIDER, footer_tab_divider_color ); + openColorPicker( COLOR_DIALOG_ID_FOOTER_TAB_DIVIDER, footer_tab_divider_color ,false); break; case R.id.btnTabDividerColorReset: @@ -394,6 +401,16 @@ public class ActAppSetting extends AppCompatActivity showFooterColor(); break; + case R.id.btnTabIndicatorColorEdit: + openColorPicker( COLOR_DIALOG_ID_FOOTER_TAB_INDICATOR, footer_tab_indicator_color ,true); + break; + + case R.id.btnTabIndicatorColorReset: + footer_tab_indicator_color = 0; + saveUIToData(); + showFooterColor(); + break; + case R.id.btnTimelineFontReset: timeline_font = ""; saveUIToData(); @@ -462,11 +479,11 @@ public class ActAppSetting extends AppCompatActivity super.onActivityResult( requestCode, resultCode, data ); } - void openColorPicker( int id, int color ){ + void openColorPicker( int id, int color ,boolean bShowAlphaSlider ){ ColorPickerDialog.Builder builder = ColorPickerDialog.newBuilder() .setDialogType( ColorPickerDialog.TYPE_CUSTOM ) .setAllowPresets( true ) - .setShowAlphaSlider( false ) + .setShowAlphaSlider( bShowAlphaSlider ) .setDialogId( id ); if( color != 0 ) builder.setColor( color ); builder.show( this ); @@ -498,7 +515,12 @@ public class ActAppSetting extends AppCompatActivity saveUIToData(); showFooterColor(); break; - + case COLOR_DIALOG_ID_FOOTER_TAB_INDICATOR: + if( color == 0 ) color = 0x01000000; + footer_tab_indicator_color = color; + saveUIToData(); + showFooterColor(); + break; } } @@ -543,6 +565,13 @@ public class ActAppSetting extends AppCompatActivity vFooterDivider1.setBackgroundColor( c ); vFooterDivider2.setBackgroundColor( c ); } + + c = footer_tab_indicator_color; + if( c == 0 ){ + vIndicator.setBackgroundColor( Styler.getAttributeColor( this, R.attr.colorAccent ) ); + }else{ + vIndicator.setBackgroundColor( c ); + } } @Override public void beforeTextChanged( CharSequence s, int start, int count, int after ){ diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java index 8c696b08..471ff5b9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.java @@ -38,7 +38,6 @@ import android.view.WindowManager; import android.widget.HorizontalScrollView; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.LinearLayout; import org.apache.commons.io.IOUtils; import org.json.JSONException; @@ -78,6 +77,7 @@ import jp.juggler.subwaytooter.util.LinkClickContext; import jp.juggler.subwaytooter.util.LogCategory; import jp.juggler.subwaytooter.util.MyClickableSpan; import jp.juggler.subwaytooter.util.Utils; +import jp.juggler.subwaytooter.view.ColumnStripLinearLayout; import jp.juggler.subwaytooter.view.GravitySnapHelper; import okhttp3.Request; import okhttp3.RequestBody; @@ -228,6 +228,8 @@ public class ActMain extends AppCompatActivity for( Column column : app_state.column_list ){ column.onResume( this ); } + + updateColumnStripSelection(-1,-1f); } private void handleSentIntent( final Intent sent_intent ){ @@ -278,7 +280,7 @@ public class ActMain extends AppCompatActivity @Override public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels ){ - + updateColumnStripSelection(position,positionOffset); } @Override public void onPageSelected( final int position ){ @@ -293,8 +295,10 @@ public class ActMain extends AppCompatActivity } } } ); + } + @Override public void onPageScrollStateChanged( int state ){ } @@ -602,7 +606,7 @@ public class ActMain extends AppCompatActivity ColumnPagerAdapter pager_adapter; View llEmpty; DrawerLayout drawer; - LinearLayout llColumnStrip; + ColumnStripLinearLayout llColumnStrip; HorizontalScrollView svColumnStrip; ImageButton btnMenu; ImageButton btnToot; @@ -655,7 +659,7 @@ public class ActMain extends AppCompatActivity btnToot = (ImageButton) findViewById( R.id.btnToot ); vFooterDivider1 = findViewById( R.id.vFooterDivider1 ); vFooterDivider2 = findViewById( R.id.vFooterDivider2 ); - llColumnStrip = (LinearLayout) findViewById( R.id.llColumnStrip ); + llColumnStrip = (ColumnStripLinearLayout) findViewById( R.id.llColumnStrip ); svColumnStrip = (HorizontalScrollView) findViewById( R.id.svColumnStrip ); btnToot.setOnClickListener( this ); @@ -733,6 +737,7 @@ public class ActMain extends AppCompatActivity @Override public void onScrolled( RecyclerView recyclerView, int dx, int dy ){ super.onScrolled( recyclerView, dx, dy ); + updateColumnStripSelection(-1,-1f); } } ); ///////tablet_pager.setHasFixedSize( true ); @@ -796,8 +801,43 @@ public class ActMain extends AppCompatActivity } svColumnStrip.requestLayout(); + updateColumnStripSelection(-1,-1f); + } + private void updateColumnStripSelection(final int position,final float positionOffset){ + handler.post( new Runnable() { + @Override public void run(){ + if( isFinishing() ) return; + + if( app_state.column_list.isEmpty() ){ + llColumnStrip.setColumnRange(-1,-1,0f); + }else if( pager_adapter != null ){ + if( position >= 0 ){ + llColumnStrip.setColumnRange( position, position, positionOffset ); + }else{ + int c = pager.getCurrentItem(); + llColumnStrip.setColumnRange( c, c, 0f ); + } + }else{ + int first = tablet_layout_manager.findFirstVisibleItemPosition(); + int last = tablet_layout_manager.findLastVisibleItemPosition(); + + if( last-first > nScreenColumn-1 ){ + last = first + nScreenColumn - 1; + } + float slide_ratio = 0f; + if( first != RecyclerView.NO_POSITION && nColumnWidth > 0 ){ + View child = tablet_layout_manager.findViewByPosition( first ); + slide_ratio = Math.abs( child.getLeft() / (float)nColumnWidth); + log.d("slide_ratio %s",slide_ratio); + } + + llColumnStrip.setColumnRange(first,last,slide_ratio); + } + } + } ); + } private void scrollColumnStrip( final int select ){ int child_count = llColumnStrip.getChildCount(); if( select < 0 || select >= child_count ){ @@ -2966,7 +3006,7 @@ public class ActMain extends AppCompatActivity int footer_button_fg_color = pref.getInt( Pref.KEY_FOOTER_BUTTON_FG_COLOR, 0 ); int footer_tab_bg_color = pref.getInt( Pref.KEY_FOOTER_TAB_BG_COLOR, 0 ); int footer_tab_divider_color = pref.getInt( Pref.KEY_FOOTER_TAB_DIVIDER_COLOR, 0 ); - + int footer_tab_indicator_color = pref.getInt( Pref.KEY_FOOTER_TAB_INDICATOR_COLOR, 0 ); int c = footer_button_bg_color; if( c == 0 ){ btnMenu.setBackgroundResource( R.drawable.btn_bg_ddd ); @@ -3003,6 +3043,9 @@ public class ActMain extends AppCompatActivity vFooterDivider1.setBackgroundColor( c ); vFooterDivider2.setBackgroundColor( c ); } + + c = footer_tab_indicator_color; + llColumnStrip.setColor(c); } ArrayList< SavedAccount > makeAccountListNonPseudo( LogCategory log ){ @@ -3196,6 +3239,7 @@ public class ActMain extends AppCompatActivity } int nScreenColumn; + int nColumnWidth; private void resizeColumnWidth(){ @@ -3248,6 +3292,7 @@ public class ActMain extends AppCompatActivity column_w = column_w_max; } + nColumnWidth = column_w; tablet_pager_adapter.setColumnWidth( column_w ); tablet_snap_helper.setColumnWidth( column_w ); } @@ -3414,4 +3459,7 @@ public class ActMain extends AppCompatActivity progress.show(); task.executeOnExecutor( App1.task_executor ); } + + + } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPost.java b/app/src/main/java/jp/juggler/subwaytooter/ActPost.java index 826dcabf..21f26961 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActPost.java +++ b/app/src/main/java/jp/juggler/subwaytooter/ActPost.java @@ -25,6 +25,7 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.text.Editable; +import android.text.Spannable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.View; @@ -32,6 +33,7 @@ import android.view.ViewTreeObserver; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.ScrollView; import android.widget.TextView; @@ -64,8 +66,10 @@ import jp.juggler.subwaytooter.table.AcctSet; import jp.juggler.subwaytooter.table.PostDraft; import jp.juggler.subwaytooter.table.SavedAccount; import jp.juggler.subwaytooter.dialog.ActionsDialog; +import jp.juggler.subwaytooter.table.TagSet; import jp.juggler.subwaytooter.util.HTMLDecoder; import jp.juggler.subwaytooter.util.LogCategory; +import jp.juggler.subwaytooter.util.MyClickableSpan; import jp.juggler.subwaytooter.view.MyEditText; import jp.juggler.subwaytooter.view.MyNetworkImageView; import jp.juggler.subwaytooter.util.PostAttachment; @@ -75,7 +79,6 @@ import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.Request; import okhttp3.RequestBody; -import okhttp3.Response; import okio.BufferedSink; public class ActPost extends AppCompatActivity implements View.OnClickListener, PostAttachment.Callback { @@ -162,11 +165,15 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, case R.id.btnMore: performMore(); break; + + case R.id.btnPlugin: + openMushroom(); } } private static final int REQUEST_CODE_ATTACHMENT = 1; private static final int REQUEST_CODE_CAMERA = 2; + private static final int REQUEST_CODE_MUSHROOM = 3; @Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){ @@ -210,6 +217,9 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, addAttachment( uri, type ); } } + }else if( requestCode == REQUEST_CODE_MUSHROOM && resultCode == RESULT_OK ){ + String text = data.getStringExtra( "replace_key" ); + applyMushroomResult( text ); } super.onActivityResult( requestCode, resultCode, data ); } @@ -240,6 +250,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, } if( savedInstanceState != null ){ + + mushroom_input = savedInstanceState.getInt( STATE_MUSHROOM_INPUT, 0 ); + mushroom_start = savedInstanceState.getInt( STATE_MUSHROOM_START, 0 ); + mushroom_end = savedInstanceState.getInt( STATE_MUSHROOM_END, 0 ); + long account_db_id = savedInstanceState.getLong( KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_ID ); if( account_db_id != SavedAccount.INVALID_ID ){ for( int i = 0, ie = account_list.size() ; i < ie ; ++ i ){ @@ -451,6 +466,10 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, protected void onSaveInstanceState( Bundle outState ){ super.onSaveInstanceState( outState ); + outState.putInt( STATE_MUSHROOM_INPUT, mushroom_input ); + outState.putInt( STATE_MUSHROOM_START, mushroom_start ); + outState.putInt( STATE_MUSHROOM_END, mushroom_end ); + if( account != null ){ outState.putLong( KEY_ACCOUNT_DB_ID, account.db_id ); } @@ -553,6 +572,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, btnPost.setOnClickListener( this ); btnRemoveReply.setOnClickListener( this ); + findViewById( R.id.btnPlugin ).setOnClickListener( this ); + for( MyNetworkImageView iv : ivMedia ){ iv.setOnClickListener( this ); iv.setDefaultImageResId( Styler.getAttributeResourceId( this, R.attr.ic_loading ) ); @@ -609,6 +630,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, } }; + static final Pattern reCharsNotTag = Pattern.compile( "[\\s\\-+.,:;/]" ); + final Runnable proc_text_changed = new Runnable() { @Override public void run(){ int start = etContent.getSelectionStart(); @@ -643,7 +666,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, } // 登場した@の数 if( count_atMark == 0 ){ - closeAcctPopup(); + // 次はAcctじゃなくてHashtagの補完を試みる + checkTag(); return; }else if( count_atMark == 1 ){ start = pos_atMark[ 0 ]; @@ -668,6 +692,37 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, popup.setList( acct_list, start, end ); } } + + private void checkTag(){ + int end = etContent.getSelectionEnd(); + + String src = etContent.getText().toString(); + int last_sharp = src.lastIndexOf( '#' ); + + if( last_sharp == - 1 || end - last_sharp < 3 ){ + closeAcctPopup(); + return; + } + + String part = src.substring( last_sharp + 1, end ); + if( reCharsNotTag.matcher( part ).find() ){ + closeAcctPopup(); + return; + } + + int limit = 100; + String s = src.substring( last_sharp + 1, end ); + ArrayList< String > tag_list = TagSet.searchPrefix( s, limit ); + log.d( "search for %s, result=%d", s, tag_list.size() ); + if( tag_list.isEmpty() ){ + closeAcctPopup(); + }else{ + if( popup == null || ! popup.isShowing() ){ + popup = new PopupAutoCompleteAcct( ActPost.this, etContent, formRoot ); + } + popup.setList( tag_list, last_sharp, end ); + } + } }; PopupAutoCompleteAcct popup; @@ -1017,7 +1072,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, } @Override - public void writeTo( BufferedSink sink ) throws IOException{ + public void writeTo( @NonNull BufferedSink sink ) throws IOException{ InputStream is = opener.open(); if( is == null ) throw new IOException( "openInputStream() failed. uri=" + uri ); @@ -1044,7 +1099,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, opener.deleteTempFile(); - if( result.object != null ){ + if( result != null && result.object != null ){ pa.attachment = TootAttachment.parse( log, result.object ); if( pa.attachment == null ){ result.error = "TootAttachment.parse failed"; @@ -1411,8 +1466,33 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, .header( "Idempotency-Key", digest ); TootApiResult result = client.request( "/api/v1/statuses", request_builder ); - if( result.object != null ){ + if( result != null && result.object != null ){ status = TootStatus.parse( log, account, result.object ); + + Spannable s = status.decoded_content; + MyClickableSpan[] span_list = s.getSpans( 0, s.length(), MyClickableSpan.class ); + if( span_list != null ){ + ArrayList< String > tag_list = new ArrayList<>(); + for( MyClickableSpan span : span_list ){ + int start = s.getSpanStart( span ); + int end = s.getSpanEnd( span ); + String text = s.subSequence( start, end ).toString(); + if( text.startsWith( "#" ) ){ + tag_list.add( text.substring( 1 ) ); + } + } + int count = tag_list.size(); + if( count > 0 ){ + TagSet.saveList( + System.currentTimeMillis() + , tag_list.toArray( new String[ count ] ) + , 0 + , count + ); + } + + } + } return result; @@ -1427,7 +1507,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, protected void onPostExecute( TootApiResult result ){ try{ progress.dismiss(); - }catch(Throwable ignored){ + }catch( Throwable ignored ){ // java.lang.IllegalArgumentException: // at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:396) // at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:322) @@ -1478,7 +1558,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, llReply.setVisibility( View.GONE ); }else{ llReply.setVisibility( View.VISIBLE ); - tvReplyTo.setText( HTMLDecoder.decodeHTML( account ,in_reply_to_text ,true,null )); + tvReplyTo.setText( HTMLDecoder.decodeHTML( account, in_reply_to_text, true, null ) ); ivReply.setCornerRadius( pref, 16f ); ivReply.setImageUrl( in_reply_to_image ); @@ -1545,9 +1625,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, @Override public void onDraftSelected( JSONObject draft ){ restoreDraft( draft ); } - }); + } ); - } static boolean check_exist( String url ){ @@ -1573,14 +1652,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, @Override protected String doInBackground( Void... params ){ String content = draft.optString( DRAFT_CONTENT ); - String content_warning = draft.optString( DRAFT_CONTENT_WARNING ); - boolean content_warning_checked = draft.optBoolean( DRAFT_CONTENT_WARNING_CHECK ); - boolean nsfw_checked = draft.optBoolean( DRAFT_NSFW_CHECK ); long account_db_id = draft.optLong( DRAFT_ACCOUNT_DB_ID ); JSONArray tmp_attachment_list = draft.optJSONArray( DRAFT_ATTACHMENT_LIST ); - long reply_id = draft.optLong( DRAFT_REPLY_ID, in_reply_to_id ); - String reply_text = draft.optString( DRAFT_REPLY_TEXT, in_reply_to_text ); - String reply_image = draft.optString( DRAFT_REPLY_IMAGE, in_reply_to_image ); account = SavedAccount.loadAccount( log, account_db_id ); if( account == null ){ @@ -1735,4 +1808,75 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener, task.executeOnExecutor( App1.task_executor ); } + ////////////////////////////////////////////////////////////////////////// + // Mushroom plugin + + private static final String STATE_MUSHROOM_INPUT = "mushroom_input"; + private static final String STATE_MUSHROOM_START = "mushroom_start"; + private static final String STATE_MUSHROOM_END = "mushroom_end"; + int mushroom_input; + int mushroom_start; + int mushroom_end; + + @NonNull String prepareMushroomText( @NonNull EditText et ){ + mushroom_start = et.getSelectionStart(); + mushroom_end = et.getSelectionEnd(); + if( mushroom_end > mushroom_start ){ + return et.getText().toString().substring( mushroom_start, mushroom_end ); + }else{ + return ""; + } + } + + void applyMushroomText( @NonNull EditText et, @NonNull String text ){ + String src = et.getText().toString(); + if( mushroom_start > src.length() ) mushroom_start = src.length(); + if( mushroom_end > src.length() ) mushroom_end = src.length(); + + StringBuilder sb = new StringBuilder(); + sb.append( src.substring( 0, mushroom_start ) ); + int new_sel_start = sb.length(); + sb.append( text ); + int new_sel_end = sb.length(); + sb.append( src.substring( mushroom_end ) ); + et.setText( sb ); + et.setSelection( new_sel_end, new_sel_end ); + } + + void openMushroom(){ + try{ + String text; + if( etContentWarning.hasFocus() ){ + mushroom_input = 1; + text = prepareMushroomText( etContentWarning ); + }else{ + mushroom_input = 0; + text = prepareMushroomText( etContent ); + } + Intent intent = new Intent( "com.adamrocker.android.simeji.ACTION_INTERCEPT" ); + intent.addCategory( "com.adamrocker.android.simeji.REPLACE" ); + intent.putExtra( "replace_key", text ); + + // Create intent to show chooser + Intent chooser = Intent.createChooser( intent, getString( R.string.select_plugin ) ); + + // Verify the intent will resolve to at least one activity + if( intent.resolveActivity( getPackageManager() ) == null ){ + Utils.showToast( this, false, R.string.plugin_not_installed ); + return; + } + startActivityForResult( chooser, REQUEST_CODE_MUSHROOM ); + + }catch( Throwable ex ){ + Utils.showToast( this, ex, R.string.plugin_not_installed ); + } + } + + private void applyMushroomResult( String text ){ + if( mushroom_input == 1 ){ + applyMushroomText( etContentWarning, text ); + }else{ + applyMushroomText( etContent, text ); + } + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/App1.java b/app/src/main/java/jp/juggler/subwaytooter/App1.java index a1fd87fb..c58416c1 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/App1.java +++ b/app/src/main/java/jp/juggler/subwaytooter/App1.java @@ -37,6 +37,7 @@ import jp.juggler.subwaytooter.table.MutedWord; import jp.juggler.subwaytooter.table.NotificationTracking; import jp.juggler.subwaytooter.table.PostDraft; import jp.juggler.subwaytooter.table.SavedAccount; +import jp.juggler.subwaytooter.table.TagSet; import jp.juggler.subwaytooter.table.UserRelation; import jp.juggler.subwaytooter.util.LogCategory; import okhttp3.CipherSuite; @@ -50,7 +51,7 @@ public class App1 extends Application { static final LogCategory log = new LogCategory( "App1" ); static final String DB_NAME = "app_db"; - static final int DB_VERSION = 14; + static final int DB_VERSION = 15; // 2017/4/25 v10 1=>2 SavedAccount に通知設定を追加 // 2017/4/25 v10 1=>2 NotificationTracking テーブルを追加 // 2017/4/29 v20 2=>5 MediaShown,ContentWarningのインデクスが間違っていたので貼り直す @@ -63,6 +64,7 @@ public class App1 extends Application { // 2017/5/17 v59 11=>12 PostDraft テーブルの追加 // 2017/5/23 v68 12=>13 SavedAccountに項目追加 // 2017/5/25 v69 13=>14 SavedAccountに項目追加 + // 2017/5/27 v73 14=>15 TagSetテーブルの追加 private static DBOpenHelper db_open_helper; @@ -120,6 +122,7 @@ public class App1 extends Application { AcctColor.onDBCreate( db ); MutedWord.onDBCreate( db ); PostDraft.onDBCreate( db ); + TagSet.onDBCreate(db); } @Override @@ -137,6 +140,7 @@ public class App1 extends Application { AcctColor.onDBUpgrade( db, oldVersion, newVersion ); MutedWord.onDBUpgrade( db, oldVersion, newVersion ); PostDraft.onDBUpgrade( db, oldVersion, newVersion ); + TagSet.onDBUpgrade( db, oldVersion, newVersion ); } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java index 4f33e203..30199f0a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java +++ b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.java @@ -308,6 +308,7 @@ public class AppDataExporter { case Pref.KEY_FOOTER_BUTTON_FG_COLOR: case Pref.KEY_FOOTER_TAB_BG_COLOR: case Pref.KEY_FOOTER_TAB_DIVIDER_COLOR: + case Pref.KEY_FOOTER_TAB_INDICATOR_COLOR: int iv = reader.nextInt(); e.putInt( k, iv ); break; diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.java b/app/src/main/java/jp/juggler/subwaytooter/Column.java index 96f2a745..1686190d 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.java @@ -33,11 +33,13 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip; import jp.juggler.subwaytooter.api.entity.TootReport; import jp.juggler.subwaytooter.api.entity.TootResults; import jp.juggler.subwaytooter.api.entity.TootStatus; +import jp.juggler.subwaytooter.api.entity.TootTag; import jp.juggler.subwaytooter.table.AcctColor; import jp.juggler.subwaytooter.table.AcctSet; import jp.juggler.subwaytooter.table.MutedApp; import jp.juggler.subwaytooter.table.MutedWord; import jp.juggler.subwaytooter.table.SavedAccount; +import jp.juggler.subwaytooter.table.TagSet; import jp.juggler.subwaytooter.table.UserRelation; import jp.juggler.subwaytooter.util.BucketList; import jp.juggler.subwaytooter.api.DuplicateMap; @@ -1267,6 +1269,7 @@ class Column implements StreamReader.Callback { HashSet< Long > who_set = new HashSet<>(); HashSet< String > acct_set = new HashSet<>(); + HashSet< String > tag_set = new HashSet<>(); { TootAccount a; TootStatus s; @@ -1278,6 +1281,11 @@ class Column implements StreamReader.Callback { acct_set.add( "@" + access_info.getFullAcct( a ) ); }else if( o instanceof TootStatus ){ s = (TootStatus) o; + if( s.tags != null ){ + for(TootTag tag : s.tags){ + tag_set.add( tag.name); + } + } a = s.account; if( a != null ){ who_set.add( a.id ); @@ -1285,6 +1293,13 @@ class Column implements StreamReader.Callback { } s = s.reblog; if( s != null ){ + + if( s.tags != null ){ + for(TootTag tag : s.tags){ + tag_set.add( tag.name); + } + } + a = s.account; if( a != null ){ who_set.add( a.id ); @@ -1302,6 +1317,13 @@ class Column implements StreamReader.Callback { // s = n.status; if( s != null ){ + + if( s.tags != null ){ + for(TootTag tag : s.tags){ + tag_set.add( tag.name); + } + } + a = s.account; if( a != null ){ who_set.add( a.id ); @@ -1309,6 +1331,13 @@ class Column implements StreamReader.Callback { } s = s.reblog; if( s != null ){ + + if( s.tags != null ){ + for(TootTag tag : s.tags){ + tag_set.add( tag.name); + } + } + a = s.account; if( a != null ){ who_set.add( a.id ); @@ -1372,7 +1401,26 @@ class Column implements StreamReader.Callback { log.d( "updateRelation: update %d acct.", n ); } - + size = tag_set.size(); + if( size > 0 ){ + String[] tag_list = new String[ size ]; + { + int n = 0; + for( String l : tag_set ){ + tag_list[ n++ ] = l; + } + } + long now = System.currentTimeMillis(); + int n = 0; + while( n < size ){ + int length = size - n; + if( length > ACCT_DB_STEP ) length = ACCT_DB_STEP; + TagSet.saveList( now, tag_list, n, length ); + n += length; + } + log.d( "updateRelation: update %d tag.", n ); + + } } void startRefreshForPost( long status_id, int refresh_after_toot ){ diff --git a/app/src/main/java/jp/juggler/subwaytooter/Pref.java b/app/src/main/java/jp/juggler/subwaytooter/Pref.java index 5752db35..43b6045b 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Pref.java +++ b/app/src/main/java/jp/juggler/subwaytooter/Pref.java @@ -37,6 +37,7 @@ public class Pref { static final String KEY_FOOTER_BUTTON_FG_COLOR = "footer_button_fg_color"; static final String KEY_FOOTER_TAB_BG_COLOR = "footer_tab_bg_color"; static final String KEY_FOOTER_TAB_DIVIDER_COLOR = "footer_tab_divider_color"; + static final String KEY_FOOTER_TAB_INDICATOR_COLOR = "footer_tab_indicator_color"; static final String KEY_DONT_USE_STREAMING = "dont_use_streaming"; static final String KEY_DONT_REFRESH_ON_RESUME = "dont_refresh_on_resume"; diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/LoginForm.java b/app/src/main/java/jp/juggler/subwaytooter/dialog/LoginForm.java index 835b9e98..420d58eb 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/LoginForm.java +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/LoginForm.java @@ -67,7 +67,7 @@ public class LoginForm { if( TextUtils.isEmpty( instance ) ){ Utils.showToast( activity, true, R.string.instance_not_specified ); return; - }else if( instance.contains( "/" )){ + }else if( instance.contains( "/" ) || instance.contains( "@" ) ){ Utils.showToast( activity, true, R.string.instance_not_need_slash ); return; } diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/TagSet.java b/app/src/main/java/jp/juggler/subwaytooter/table/TagSet.java new file mode 100644 index 00000000..7d62dc67 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/table/TagSet.java @@ -0,0 +1,144 @@ +package jp.juggler.subwaytooter.table; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.support.annotation.NonNull; + +import java.util.ArrayList; + +import jp.juggler.subwaytooter.App1; +import jp.juggler.subwaytooter.util.LogCategory; + +public class TagSet { + + private static final LogCategory log = new LogCategory( "TagSet" ); + + private static final String table = "tag_set"; + private static final String COL_TIME_SAVE = "time_save"; + private static final String COL_TAG = "tag"; // タグ。先頭の#を含まない + + public static void onDBCreate( SQLiteDatabase db ){ + log.d( "onDBCreate!" ); + db.execSQL( + "create table if not exists " + table + + "(_id INTEGER PRIMARY KEY" + + "," + COL_TIME_SAVE + " integer not null" + + "," + COL_TAG + " text not null" + + ")" + ); + db.execSQL( + "create unique index if not exists " + table + "_tag on " + table + "(" + COL_TAG + ")" + ); + db.execSQL( + "create index if not exists " + table + "_time on " + table + "(" + COL_TIME_SAVE + ")" + ); + } + + public static void onDBUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){ + if( oldVersion < 15 && newVersion >= 15 ){ + onDBCreate( db ); + } + } + + public static void deleteOld( long now ){ + try{ + // 古いデータを掃除する + long expire = now - 86400000L * 365; + App1.getDB().delete( table, COL_TIME_SAVE + " prefix_search_where_arg = new ThreadLocal< String[] >() { + @Override protected String[] initialValue(){ + return new String[ 1 ]; + } + }; + + private static String makePattern( String src ){ + StringBuilder sb = new StringBuilder(); + for( int i = 0, ie = src.length() ; i < ie ; ++ i ){ + char c = src.charAt( i ); + if( c == '%' || c == '_' || c == '$' ){ + sb.append( '$' ); + } + sb.append( c ); + } + // 前方一致検索にするため、末尾に%をつける + sb.append( '%' ); + return sb.toString(); + } + + @NonNull public static ArrayList< String > searchPrefix( @NonNull String prefix ,int limit){ + try{ + String[] where_arg = prefix_search_where_arg.get(); + where_arg[ 0 ] = makePattern( prefix ); + Cursor cursor = App1.getDB().query( table, null, prefix_search_where, where_arg, null, null, COL_TAG + " asc limit "+limit ); + if( cursor != null ){ + try{ + ArrayList< String > dst = new ArrayList<>( cursor.getCount() ); + int idx_acct = cursor.getColumnIndex( COL_TAG ); + while( cursor.moveToNext() ){ + dst.add( "#"+cursor.getString( idx_acct ) ); + } + return dst; + }finally{ + cursor.close(); + } + } + }catch( Throwable ex ){ + ex.printStackTrace(); + log.e( ex, "searchPrefix failed." ); + } + return new ArrayList<>(); + } + +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/view/ColumnStripLinearLayout.java b/app/src/main/java/jp/juggler/subwaytooter/view/ColumnStripLinearLayout.java new file mode 100644 index 00000000..1728c870 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/view/ColumnStripLinearLayout.java @@ -0,0 +1,83 @@ +package jp.juggler.subwaytooter.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import jp.juggler.subwaytooter.R; +import jp.juggler.subwaytooter.Styler; + +public class ColumnStripLinearLayout extends LinearLayout { + public ColumnStripLinearLayout( Context context ){ + super( context ); + init(); + } + + public ColumnStripLinearLayout( Context context, @Nullable AttributeSet attrs ){ + super( context, attrs ); + init(); + } + + public ColumnStripLinearLayout( Context context, @Nullable AttributeSet attrs, int defStyleAttr ){ + super( context, attrs, defStyleAttr ); + init(); + } + + final Paint paint = new Paint(); + final Rect rect = new Rect(); + int h ; + void init(){ + h = (int)(0.5f + 2f * getResources().getDisplayMetrics().density ); + } + + int first; + int last; + float slide_ratio; + + public void setColumnRange(int first,int last,float slide_ratio){ + if( this.first == first && this.last == last && this.slide_ratio == slide_ratio ) return; + this.first = first; + this.last = last; + this.slide_ratio = slide_ratio; + invalidate(); + } + + int color; + public void setColor( int color ){ + if( color == 0 ) color = Styler.getAttributeColor(getContext(), R.attr.colorAccent); + if( this.color == color ) return; + this.color = color; + invalidate(); + } + + + @Override protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if( first < 0 || last >= getChildCount() ) return; + + View child = getChildAt( first ); + rect.left = child.getLeft(); + child = getChildAt( last ); + rect.right = child.getRight(); + + if( slide_ratio != 0f){ + child = getChildAt( first ); + int w = child.getWidth(); + int slide = (int)(0.5f + slide_ratio * w); + rect.left += slide; + rect.right += slide; + } + + rect.top = 0; + rect.bottom = h; + paint.setColor( color ); + canvas.drawRect( rect,paint ); + } + +} diff --git a/app/src/main/res/drawable-hdpi/ic_plugin.png b/app/src/main/res/drawable-hdpi/ic_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..ea629beee9684b9616429f4ed55b3a62eed27c72 GIT binary patch literal 613 zcmV-r0-F7aP)&m^7QsJEJqw zYPCFy_BILt`|Z|P02KY#DCc)V|Ct!+e5cbnCI{CX06u%Y-jQ(t%H{GYIk@2f@a;Ix zi3K3Kkaif3l1im=&I`bG-Lp7diIx}{yClP7)Gmz2$sy-=JJBIMBuo0A58KMl!*L$R z(h%5I0zpxD(&c<0W4?6F>x_ON3rnea zkh1rfLGGl=lZphON=g?Qp2Yjy37l-gCOf(DwB2rBkm3)`6l2R~Z0YgwMt6Te zlx{n+ETx!COjX#bZvcRmbT8x{0Jc>F`d}t|iFIv46XP0F*m(@_x}(p|hpzBJhRD-{ zZy7th3CJ2ZfVjqtt$k1lLU|85K@SY*b41_gI;aFPj(aBv{7Uqq%2+b@?*TC8>N6I* z$PEBEjz$mTPBH{AQvleV&~R}qRx}%#0l@AQ7WokRQ5D(AvIH>a)c9AbZyrm?1S_Xalz?^! zl+eQgT`RKrtnB=LPweQoKm$xh?nzLJ~2iPb{Bp(clP_-Y-VOtD46kkY`5D}!7&PNfC=!3F}OVj;U}OCHX*dcZ#Wxhg0Bbz zYt}mfajy~L14dlMm*{zqGq3N=&R`n!>NZBXDW) zFqR3#Z|E)M#~{|S!DSla7YT(O0$0lfCO_mTKET#_=3NE`T%|KXhskGPK<%w%3>X8( zfH9D}firLi`d~n*-HB;f4rQQ{N}WV4V;#&gG_YGciPB~yIy>d|6caDNJm*JCw5Khd z-MC?JJ~moCHYUF_Ffi6JU^6h_DPNxb1p^KP0~1y8H719+p(5U5aDYXAXsaYL@z1|y zEOzw+k8S{)k%^3M+B%>MdSD2qlt8i+zoZ>66Q#5-s@N8D|K5N@dBWB_@R=JBVrwC_ z)112j@piIes@NG<1M7YN{GPaV$)+eX1Jfi82IUeb3j;~NxC9IjR0O)pZ6qeXNG#No zh&zyo`=nFvS0#3#D-pM!Fy@E2;@AB~P^F#lc50GcIubGOV`6=RXK50H!AaI3Fn4{kpY)9ZLAc?(C&@r8DyRPeVY+6T9W8?gQ z^*um={9)?qjvtW-ERRZnL(NgIfRmLqd77qYNWO|tgj-HlooxtcNIpZWJmL3eK)IWC zo)A>kd*B$wh+GL!LKY_BS|7m(uqF;AY>RX~3IWyx3$x2Hhiu!40N#Ph7KDZQi&1WP zAjz`qZos)_7WEdky&U&kNAJcWqqe9ww-9*snvAIguR|G&K4IB&ZGO)faJ$>&v XO(KRUixnyS00000NkvXXu0mjfwUE2V literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_plugin_dark.png b/app/src/main/res/drawable-mdpi/ic_plugin_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..365b5e7dcf07284654fc97043157a35274133287 GIT binary patch literal 355 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 JPDHLkV1hB`(=Px3 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/act_app_setting.xml b/app/src/main/res/layout/act_app_setting.xml index 8f3a3111..4c84bc14 100644 --- a/app/src/main/res/layout/act_app_setting.xml +++ b/app/src/main/res/layout/act_app_setting.xml @@ -363,83 +363,93 @@ - + android:layout_height="wrap_content"> - - - - - - - - + + + + + + + - - - + + - + - - + + + + + + @@ -557,6 +567,33 @@ + + + + + + + +