This commit is contained in:
tateisu 2017-05-27 07:36:08 +09:00
parent 30147b9ed5
commit 969654341f
28 changed files with 672 additions and 105 deletions

View File

@ -1,6 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="tateisu">
<words>
<w>adamrocker</w>
<w>dont</w>
<w>emoji</w>
<w>emojione</w>
@ -18,6 +19,7 @@
<w>reblogged</w>
<w>reblogs</w>
<w>sephiroth</w>
<w>simeji</w>
<w>styler</w>
<w>subwaytooter</w>
<w>swipy</w>

View File

@ -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"
}

View File

@ -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 ){

View File

@ -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 );
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}

View File

@ -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;

View File

@ -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 ){

View File

@ -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";

View File

@ -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;
}

View File

@ -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 + "<?", new String[]{ Long.toString( expire ) } );
}catch( Throwable ex ){
log.e( ex, "deleteOld failed." );
}
}
// public static void save1( long now, String acct ){
// try{
//
// ContentValues cv = new ContentValues();
// cv.put( COL_TIME_SAVE, now );
// cv.put( COL_ACCT, acct );
// App1.getDB().replace( table, null, cv );
// }catch( Throwable ex ){
// log.e( ex, "save failed." );
// }
// }
public static void saveList( long now, String[] src_list, int offset, int length ){
try{
ContentValues cv = new ContentValues();
cv.put( COL_TIME_SAVE, now );
boolean bOK = false;
SQLiteDatabase db = App1.getDB();
db.execSQL( "BEGIN TRANSACTION" );
try{
for( int i = 0 ; i < length ; ++ i ){
String acct = src_list[ i + offset ];
cv.put( COL_TAG, acct );
db.replace( table, null, cv );
}
bOK = true;
}catch( Throwable ex ){
ex.printStackTrace();
log.e( ex, "saveList failed." );
}
if( bOK ){
db.execSQL( "COMMIT TRANSACTION" );
}else{
db.execSQL( "ROLLBACK TRANSACTION" );
}
}catch( Throwable ex ){
ex.printStackTrace();
log.e( ex, "saveList failed." );
}
}
private static final String prefix_search_where = COL_TAG + " like ? escape '$'";
private static final ThreadLocal< String[] > 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<>();
}
}

View File

@ -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 );
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -363,83 +363,93 @@
<LinearLayout style="@style/setting_row_form">
<LinearLayout
android:id="@+id/llFooterBG"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?attr/colorColumnStripBackground"
android:orientation="horizontal"
>
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivFooterMenu"
android:layout_width="48dp"
<LinearLayout
android:id="@+id/llFooterBG"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
android:importantForAccessibility="no"
app:srcCompat="?attr/ic_hamburger"
/>
<View
android:id="@+id/vFooterDivider1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/colorImageButton"
/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
/>
<HorizontalScrollView
android:id="@+id/svColumnStrip"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:cacheColorHint="#00000000"
android:fadingEdge="horizontal"
android:fadingEdgeLength="20dp"
android:fillViewport="true"
android:scrollbars="none"
android:background="?attr/colorColumnStripBackground"
android:orientation="horizontal"
>
<LinearLayout
android:id="@+id/llColumnStrip"
android:layout_width="wrap_content"
<ImageView
android:id="@+id/ivFooterMenu"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
android:importantForAccessibility="no"
app:srcCompat="?attr/ic_hamburger"
/>
<View
android:id="@+id/vFooterDivider1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="?attr/colorImageButton"
/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
/>
<HorizontalScrollView
android:id="@+id/svColumnStrip"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:cacheColorHint="#00000000"
android:fadingEdge="horizontal"
android:fadingEdgeLength="20dp"
android:fillViewport="true"
android:scrollbars="none"
>
<LinearLayout
android:id="@+id/llColumnStrip"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
</LinearLayout>
</HorizontalScrollView>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
</HorizontalScrollView>
<View
android:id="@+id/vFooterDivider2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/colorImageButton"
/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
/>
<ImageView
android:id="@+id/ivFooterToot"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
android:contentDescription="@string/toot"
app:srcCompat="?attr/ic_edit"
/>
</LinearLayout>
<View
android:id="@+id/vFooterDivider2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/colorImageButton"
/>
<ImageView
android:id="@+id/ivFooterToot"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
android:contentDescription="@string/toot"
app:srcCompat="?attr/ic_edit"
/>
</LinearLayout>
<View android:layout_width="48dp"
android:layout_height="2dp"
android:layout_gravity="center_horizontal"
tools:background="#f00"
android:id="@+id/vIndicator"/>
</FrameLayout>
</LinearLayout>
@ -557,6 +567,33 @@
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/tab_indicator_color"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnTabIndicatorColorEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
android:textAllCaps="false"
/>
<Button
android:id="@+id/btnTabIndicatorColorReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
@ -678,11 +715,13 @@
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_horizontal_stretch"
android:text="@string/custom_stream_listener_desc"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<!--<TextView-->

View File

@ -87,7 +87,7 @@
android:id="@+id/svColumnStrip"
>
<LinearLayout
<jp.juggler.subwaytooter.view.ColumnStripLinearLayout
android:id="@+id/llColumnStrip"
android:layout_width="wrap_content"
android:layout_height="match_parent"

View File

@ -248,6 +248,16 @@
tools:src="?attr/ic_public"
/>
<ImageButton
android:id="@+id/btnPlugin"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="4dp"
android:background="@drawable/btn_bg_transparent"
android:contentDescription="@string/plugin"
android:src="?attr/ic_plugin"
/>
<ImageButton
android:id="@+id/btnMore"
android:layout_width="48dp"

View File

@ -165,7 +165,7 @@
<string name="image_pick">Choisir une image</string>
<string name="instance">Instance</string>
<string name="instance_hint">Ex) mastodon.social</string>
<string name="instance_not_need_slash">Veuillez entrer seulement le nom de l\'hôte de l\'instance, sans « / ».</string>
<string name="instance_not_need_slash">Veuillez entrer seulement le nom de l\'hôte de l\'instance, sans « / », « \@ ».</string>
<string name="instance_not_specified">Instance non spécifiée</string>
<string name="it_is_you">C\'est vous.</string>
<string name="last_selection">Dernière sélection</string>
@ -338,6 +338,10 @@
<string name="custom_stream_listener_desc">This setting is for advanced users. If you are not sure, please don\'t edit.</string>
<string name="custom_stream_listener_was_reset">Custom notification listener was reset.</string>
<string name="input_url_and_secret_then_test">Input URL and Secret, then tap Test button.</string>
<string name="tab_indicator_color">Tab\'s indicator color</string>
<string name="plugin">Plugin</string>
<string name="select_plugin">Select plugin</string>
<string name="plugin_not_installed">Plugin is not installed.</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

@ -204,7 +204,7 @@
<!---->
<string name="conversation_around">会話の流れ(id:%1$d)</string>
<!---->
<string name="instance_not_need_slash">インスタンスには\'/\'を含めずに、ホスト名だけを指定してください</string>
<string name="instance_not_need_slash">インスタンスには\'/\',\'\@\'を含めずに、ホスト名だけを指定してください</string>
<!---->
<string name="mail_address_not_contains_at_mark">メールアドレスが不十分です。\'@\'や\'.\'がありません</string>
<!---->
@ -625,5 +625,9 @@
<string name="test">テスト</string>
<string name="custom_stream_listener_was_reset">カスタム通知リスナを使わないようにしました</string>
<string name="input_url_and_secret_then_test">URLとシークレットを入力してテストボタンを押してください</string>
<string name="tab_indicator_color">タブのインジケータの色</string>
<string name="plugin">プラグイン</string>
<string name="select_plugin">プラグインの選択</string>
<string name="plugin_not_installed">プラグインがインストールされてません</string>
</resources>

View File

@ -116,5 +116,6 @@
<attr name="ic_hamburger" format="reference" />
<attr name="ic_delete" format="reference" />
<attr name="ic_plugin" format="reference" />
</resources>

View File

@ -104,8 +104,8 @@
<string name="column">Column</string>
<string name="conversation_view">Conversation view</string>
<string name="conversation_around">Conversation around toot:%1$d</string>
<string name="instance_not_need_slash">Hostname of the instance, without \'/\'.</string>
<string name="mail_address_not_contains_at_mark">Use emails that contains \'@\' and \'.\'</string>
<string name="instance_not_need_slash">Hostname of the instance, without \'/\', \'\@\'.</string>
<string name="mail_address_not_contains_at_mark">Use emails that contains \'\@\' and \'.\'</string>
<string name="instance_hint">ex) mastodon.social</string>
<string name="mail_hint">ex) your@e-mail.address</string>
<string name="unfollow">Unfollow</string>
@ -334,4 +334,8 @@
<string name="custom_stream_listener_desc">This setting is for advanced users. If you are not sure, please don\'t edit.</string>
<string name="custom_stream_listener_was_reset">Custom notification listener was reset.</string>
<string name="input_url_and_secret_then_test">Input URL and Secret, then tap Test button.</string>
<string name="tab_indicator_color">Tab\'s indicator color</string>
<string name="plugin">Plugin</string>
<string name="select_plugin">Select plugin</string>
<string name="plugin_not_installed">Plugin is not installed.</string>
</resources>

View File

@ -84,6 +84,7 @@
<item name="ic_hamburger">@drawable/ic_hamburger</item>
<item name="ic_delete">@drawable/ic_delete</item>
<item name="ic_plugin">@drawable/ic_plugin</item>
</style>
@ -173,7 +174,7 @@
<item name="ic_hamburger">@drawable/ic_hamburger_dark</item>
<item name="ic_delete">@drawable/ic_delete_dark</item>
<item name="ic_plugin">@drawable/ic_plugin_dark</item>
</style>