parent
aac510aca5
commit
4ba7cd321a
|
@ -9,8 +9,8 @@ android {
|
||||||
applicationId "jp.juggler.subwaytooter"
|
applicationId "jp.juggler.subwaytooter"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 101
|
versionCode 102
|
||||||
versionName "1.0.1"
|
versionName "1.0.2"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@ public class ActAppSetting extends AppCompatActivity
|
||||||
Switch swPriorChrome;
|
Switch swPriorChrome;
|
||||||
Switch swPostButtonBarTop;
|
Switch swPostButtonBarTop;
|
||||||
Switch swDontDuplicationCheck ;
|
Switch swDontDuplicationCheck ;
|
||||||
|
Switch swQuickTootBar;
|
||||||
|
|
||||||
Spinner spBackButtonAction;
|
Spinner spBackButtonAction;
|
||||||
Spinner spUITheme;
|
Spinner spUITheme;
|
||||||
|
@ -191,6 +192,9 @@ public class ActAppSetting extends AppCompatActivity
|
||||||
swDontDuplicationCheck = (Switch) findViewById( R.id.swDontDuplicationCheck );
|
swDontDuplicationCheck = (Switch) findViewById( R.id.swDontDuplicationCheck );
|
||||||
swDontDuplicationCheck.setOnCheckedChangeListener( this );
|
swDontDuplicationCheck.setOnCheckedChangeListener( this );
|
||||||
|
|
||||||
|
swQuickTootBar = (Switch) findViewById( R.id.swQuickTootBar );
|
||||||
|
swQuickTootBar.setOnCheckedChangeListener( this );
|
||||||
|
|
||||||
cbNotificationSound = (CheckBox) findViewById( R.id.cbNotificationSound );
|
cbNotificationSound = (CheckBox) findViewById( R.id.cbNotificationSound );
|
||||||
cbNotificationVibration = (CheckBox) findViewById( R.id.cbNotificationVibration );
|
cbNotificationVibration = (CheckBox) findViewById( R.id.cbNotificationVibration );
|
||||||
cbNotificationLED = (CheckBox) findViewById( R.id.cbNotificationLED );
|
cbNotificationLED = (CheckBox) findViewById( R.id.cbNotificationLED );
|
||||||
|
@ -327,6 +331,7 @@ public class ActAppSetting extends AppCompatActivity
|
||||||
swDontCropMediaThumb.setChecked( pref.getBoolean( Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL, false ) );
|
swDontCropMediaThumb.setChecked( pref.getBoolean( Pref.KEY_DONT_CROP_MEDIA_THUMBNAIL, false ) );
|
||||||
swPostButtonBarTop.setChecked( pref.getBoolean( Pref.KEY_POST_BUTTON_BAR_AT_TOP, false ) );
|
swPostButtonBarTop.setChecked( pref.getBoolean( Pref.KEY_POST_BUTTON_BAR_AT_TOP, false ) );
|
||||||
swDontDuplicationCheck.setChecked( pref.getBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, false ) );
|
swDontDuplicationCheck.setChecked( pref.getBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, false ) );
|
||||||
|
swQuickTootBar.setChecked( pref.getBoolean( Pref.KEY_QUICK_TOOT_BAR, false ) );
|
||||||
|
|
||||||
// Switch with default true
|
// Switch with default true
|
||||||
swDisableFastScroller.setChecked( pref.getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, true ) );
|
swDisableFastScroller.setChecked( pref.getBoolean( Pref.KEY_DISABLE_FAST_SCROLLER, true ) );
|
||||||
|
@ -387,6 +392,7 @@ public class ActAppSetting extends AppCompatActivity
|
||||||
.putBoolean( Pref.KEY_PRIOR_CHROME, swPriorChrome.isChecked() )
|
.putBoolean( Pref.KEY_PRIOR_CHROME, swPriorChrome.isChecked() )
|
||||||
.putBoolean( Pref.KEY_POST_BUTTON_BAR_AT_TOP, swPostButtonBarTop.isChecked() )
|
.putBoolean( Pref.KEY_POST_BUTTON_BAR_AT_TOP, swPostButtonBarTop.isChecked() )
|
||||||
.putBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, swDontDuplicationCheck.isChecked() )
|
.putBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, swDontDuplicationCheck.isChecked() )
|
||||||
|
.putBoolean( Pref.KEY_QUICK_TOOT_BAR, swQuickTootBar.isChecked() )
|
||||||
|
|
||||||
|
|
||||||
.putBoolean( Pref.KEY_NOTIFICATION_SOUND, cbNotificationSound.isChecked() )
|
.putBoolean( Pref.KEY_NOTIFICATION_SOUND, cbNotificationSound.isChecked() )
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.text.TextUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.JsonReader;
|
import android.util.JsonReader;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.support.design.widget.NavigationView;
|
import android.support.design.widget.NavigationView;
|
||||||
import android.support.v4.view.GravityCompat;
|
import android.support.v4.view.GravityCompat;
|
||||||
|
@ -35,9 +36,11 @@ import android.view.MenuItem;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.HorizontalScrollView;
|
import android.widget.HorizontalScrollView;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -77,9 +80,11 @@ import jp.juggler.subwaytooter.dialog.ActionsDialog;
|
||||||
import jp.juggler.subwaytooter.util.LinkClickContext;
|
import jp.juggler.subwaytooter.util.LinkClickContext;
|
||||||
import jp.juggler.subwaytooter.util.LogCategory;
|
import jp.juggler.subwaytooter.util.LogCategory;
|
||||||
import jp.juggler.subwaytooter.util.MyClickableSpan;
|
import jp.juggler.subwaytooter.util.MyClickableSpan;
|
||||||
|
import jp.juggler.subwaytooter.util.PostHelper;
|
||||||
import jp.juggler.subwaytooter.util.Utils;
|
import jp.juggler.subwaytooter.util.Utils;
|
||||||
import jp.juggler.subwaytooter.view.ColumnStripLinearLayout;
|
import jp.juggler.subwaytooter.view.ColumnStripLinearLayout;
|
||||||
import jp.juggler.subwaytooter.view.GravitySnapHelper;
|
import jp.juggler.subwaytooter.view.GravitySnapHelper;
|
||||||
|
import jp.juggler.subwaytooter.view.MyEditText;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
|
|
||||||
|
@ -160,6 +165,8 @@ public class ActMain extends AppCompatActivity
|
||||||
@Override protected void onDestroy(){
|
@Override protected void onDestroy(){
|
||||||
log.d( "onDestroy" );
|
log.d( "onDestroy" );
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
post_helper.onDestroy();
|
||||||
|
|
||||||
// このアクティビティに関連する ColumnViewHolder への参照を全カラムから除去する
|
// このアクティビティに関連する ColumnViewHolder への参照を全カラムから除去する
|
||||||
for( Column c : app_state.column_list ){
|
for( Column c : app_state.column_list ){
|
||||||
c.removeColumnViewHolderByActivity( this );
|
c.removeColumnViewHolderByActivity( this );
|
||||||
|
@ -239,17 +246,7 @@ public class ActMain extends AppCompatActivity
|
||||||
// 各カラムのアカウント設定を読み直す
|
// 各カラムのアカウント設定を読み直す
|
||||||
reloadAccountSetting();
|
reloadAccountSetting();
|
||||||
|
|
||||||
if( ! TextUtils.isEmpty( posted_acct ) ){
|
refreshAfterPost();
|
||||||
int refresh_after_toot = pref.getInt( Pref.KEY_REFRESH_AFTER_TOOT, 0 );
|
|
||||||
if( refresh_after_toot != Pref.RAT_DONT_REFRESH ){
|
|
||||||
for( Column column : app_state.column_list ){
|
|
||||||
SavedAccount a = column.access_info;
|
|
||||||
if( ! Utils.equalsNullable( a.acct, posted_acct ) ) continue;
|
|
||||||
column.startRefreshForPost( posted_status_id, refresh_after_toot );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
posted_acct = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri uri = ActCallback.last_uri.getAndSet( null );
|
Uri uri = ActCallback.last_uri.getAndSet( null );
|
||||||
if( uri != null ){
|
if( uri != null ){
|
||||||
|
@ -268,6 +265,20 @@ public class ActMain extends AppCompatActivity
|
||||||
updateColumnStripSelection( - 1, - 1f );
|
updateColumnStripSelection( - 1, - 1f );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void refreshAfterPost(){
|
||||||
|
if( ! TextUtils.isEmpty( posted_acct ) ){
|
||||||
|
int refresh_after_toot = pref.getInt( Pref.KEY_REFRESH_AFTER_TOOT, 0 );
|
||||||
|
if( refresh_after_toot != Pref.RAT_DONT_REFRESH ){
|
||||||
|
for( Column column : app_state.column_list ){
|
||||||
|
SavedAccount a = column.access_info;
|
||||||
|
if( ! Utils.equalsNullable( a.acct, posted_acct ) ) continue;
|
||||||
|
column.startRefreshForPost( posted_status_id, refresh_after_toot );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
posted_acct = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Intent sent_intent2;
|
static Intent sent_intent2;
|
||||||
|
|
||||||
private void handleSentIntent( final Intent intent ){
|
private void handleSentIntent( final Intent intent ){
|
||||||
|
@ -334,9 +345,56 @@ public class ActMain extends AppCompatActivity
|
||||||
performTootButton();
|
performTootButton();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case R.id.btnQuickToot:
|
||||||
|
performQuickPost(null);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void performQuickPost(SavedAccount account){
|
||||||
|
|
||||||
|
if( account == null ){
|
||||||
|
if( pager_adapter != null ){
|
||||||
|
Column c = app_state.column_list.get( pager.getCurrentItem() );
|
||||||
|
if( ! c.access_info.isPseudo() ){
|
||||||
|
account = c.access_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( account == null ){
|
||||||
|
AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() {
|
||||||
|
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
||||||
|
performQuickPost( ai );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String visibility = account.visibility;
|
||||||
|
if( TextUtils.isEmpty( visibility ) ){
|
||||||
|
visibility = TootStatus.VISIBILITY_PUBLIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
post_helper.content = etQuickToot.getText().toString().trim();
|
||||||
|
post_helper.spoiler_text = null;
|
||||||
|
post_helper.visibility = visibility;
|
||||||
|
post_helper.bNSFW = false;
|
||||||
|
post_helper.in_reply_to_id = - 1L;
|
||||||
|
post_helper.attachment_list = null;
|
||||||
|
|
||||||
|
Utils.hideKeyboard( this,etQuickToot );
|
||||||
|
post_helper.post( account, false, false, new PostHelper.Callback() {
|
||||||
|
@Override public void onPostComplete( SavedAccount target_account, TootStatus status ){
|
||||||
|
etQuickToot.setText("");
|
||||||
|
posted_acct = target_account.acct;
|
||||||
|
posted_status_id =status.id;
|
||||||
|
refreshAfterPost();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels ){
|
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels ){
|
||||||
updateColumnStripSelection( position, positionOffset );
|
updateColumnStripSelection( position, positionOffset );
|
||||||
|
@ -430,6 +488,7 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
}else if( requestCode == REQUEST_CODE_POST ){
|
}else if( requestCode == REQUEST_CODE_POST ){
|
||||||
if( data != null ){
|
if( data != null ){
|
||||||
|
etQuickToot.setText("");
|
||||||
posted_acct = data.getStringExtra( ActPost.EXTRA_POSTED_ACCT );
|
posted_acct = data.getStringExtra( ActPost.EXTRA_POSTED_ACCT );
|
||||||
posted_status_id = data.getLongExtra( ActPost.EXTRA_POSTED_STATUS_ID, 0L );
|
posted_status_id = data.getLongExtra( ActPost.EXTRA_POSTED_STATUS_ID, 0L );
|
||||||
}
|
}
|
||||||
|
@ -690,6 +749,12 @@ public class ActMain extends AppCompatActivity
|
||||||
|
|
||||||
boolean dont_crop_media_thumbnail;
|
boolean dont_crop_media_thumbnail;
|
||||||
|
|
||||||
|
View llQuickTootBar;
|
||||||
|
MyEditText etQuickToot;
|
||||||
|
ImageButton btnQuickToot;
|
||||||
|
PostHelper post_helper;
|
||||||
|
|
||||||
|
|
||||||
void initUI(){
|
void initUI(){
|
||||||
setContentView( R.layout.act_main );
|
setContentView( R.layout.act_main );
|
||||||
|
|
||||||
|
@ -727,12 +792,30 @@ public class ActMain extends AppCompatActivity
|
||||||
vFooterDivider2 = findViewById( R.id.vFooterDivider2 );
|
vFooterDivider2 = findViewById( R.id.vFooterDivider2 );
|
||||||
llColumnStrip = (ColumnStripLinearLayout) findViewById( R.id.llColumnStrip );
|
llColumnStrip = (ColumnStripLinearLayout) findViewById( R.id.llColumnStrip );
|
||||||
svColumnStrip = (HorizontalScrollView) findViewById( R.id.svColumnStrip );
|
svColumnStrip = (HorizontalScrollView) findViewById( R.id.svColumnStrip );
|
||||||
|
llQuickTootBar = findViewById( R.id.llQuickTootBar );
|
||||||
|
etQuickToot = (MyEditText) findViewById( R.id. etQuickToot );
|
||||||
|
btnQuickToot = (ImageButton)findViewById( R.id. btnQuickToot );
|
||||||
|
|
||||||
|
if( !pref.getBoolean( Pref.KEY_QUICK_TOOT_BAR ,false ) ){
|
||||||
|
llQuickTootBar.setVisibility( View.GONE );
|
||||||
|
}
|
||||||
|
|
||||||
btnToot.setOnClickListener( this );
|
btnToot.setOnClickListener( this );
|
||||||
btnMenu.setOnClickListener( this );
|
btnMenu.setOnClickListener( this );
|
||||||
|
btnQuickToot.setOnClickListener( this );
|
||||||
|
etQuickToot.setOnEditorActionListener( new TextView.OnEditorActionListener() {
|
||||||
|
@Override public boolean onEditorAction( TextView v, int actionId, KeyEvent event ){
|
||||||
|
if( actionId == EditorInfo.IME_ACTION_SEND ){
|
||||||
|
btnQuickToot.performClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} );
|
||||||
svColumnStrip.setHorizontalFadingEdgeEnabled( true );
|
svColumnStrip.setHorizontalFadingEdgeEnabled( true );
|
||||||
|
|
||||||
|
post_helper = new PostHelper( this,pref,app_state.handler );
|
||||||
|
|
||||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||||
|
|
||||||
float density = dm.density;
|
float density = dm.density;
|
||||||
|
@ -814,6 +897,11 @@ public class ActMain extends AppCompatActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
showFooterColor();
|
showFooterColor();
|
||||||
|
|
||||||
|
post_helper.attachEditText( findViewById( R.id.llFormRoot ), etQuickToot, true,new PostHelper.Callback2() {
|
||||||
|
@Override public void onTextUpdate(){
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateColumnStrip(){
|
void updateColumnStrip(){
|
||||||
|
@ -1859,24 +1947,26 @@ public class ActMain extends AppCompatActivity
|
||||||
};
|
};
|
||||||
|
|
||||||
private void performTootButton(){
|
private void performTootButton(){
|
||||||
|
post_helper.closeAcctPopup();
|
||||||
|
final String initial_text = etQuickToot.getText().toString();
|
||||||
if( pager_adapter != null ){
|
if( pager_adapter != null ){
|
||||||
Column c = pager_adapter.getColumn( pager.getCurrentItem() );
|
Column c = pager_adapter.getColumn( pager.getCurrentItem() );
|
||||||
if( c != null && ! c.access_info.isPseudo() ){
|
if( c != null && ! c.access_info.isPseudo() ){
|
||||||
ActPost.open( this, REQUEST_CODE_POST, c.access_info.db_id, "" );
|
ActPost.open( this, REQUEST_CODE_POST, c.access_info.db_id, initial_text );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
long db_id = pref.getLong( Pref.KEY_TABLET_TOOT_DEFAULT_ACCOUNT, - 1L );
|
long db_id = pref.getLong( Pref.KEY_TABLET_TOOT_DEFAULT_ACCOUNT, - 1L );
|
||||||
SavedAccount a = SavedAccount.loadAccount( log, db_id );
|
SavedAccount a = SavedAccount.loadAccount( log, db_id );
|
||||||
if( a != null ){
|
if( a != null ){
|
||||||
ActPost.open( this, REQUEST_CODE_POST, a.db_id, "" );
|
ActPost.open( this, REQUEST_CODE_POST, a.db_id, initial_text );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() {
|
AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() {
|
||||||
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
|
||||||
ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, "" );
|
ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, initial_text );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
@ -3289,28 +3379,34 @@ public class ActMain extends AppCompatActivity
|
||||||
if( c == 0 ){
|
if( c == 0 ){
|
||||||
btnMenu.setBackgroundResource( R.drawable.btn_bg_ddd );
|
btnMenu.setBackgroundResource( R.drawable.btn_bg_ddd );
|
||||||
btnToot.setBackgroundResource( R.drawable.btn_bg_ddd );
|
btnToot.setBackgroundResource( R.drawable.btn_bg_ddd );
|
||||||
|
btnQuickToot.setBackgroundResource( R.drawable.btn_bg_ddd );
|
||||||
}else{
|
}else{
|
||||||
int fg = ( footer_button_fg_color != 0
|
int fg = ( footer_button_fg_color != 0
|
||||||
? footer_button_fg_color
|
? footer_button_fg_color
|
||||||
: Styler.getAttributeColor( this, R.attr.colorRippleEffect ) );
|
: Styler.getAttributeColor( this, R.attr.colorRippleEffect ) );
|
||||||
ViewCompat.setBackground( btnToot, Styler.getAdaptiveRippleDrawable( c, fg ) );
|
ViewCompat.setBackground( btnToot, Styler.getAdaptiveRippleDrawable( c, fg ) );
|
||||||
ViewCompat.setBackground( btnMenu, Styler.getAdaptiveRippleDrawable( c, fg ) );
|
ViewCompat.setBackground( btnMenu, Styler.getAdaptiveRippleDrawable( c, fg ) );
|
||||||
|
ViewCompat.setBackground( btnQuickToot, Styler.getAdaptiveRippleDrawable( c, fg ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
c = footer_button_fg_color;
|
c = footer_button_fg_color;
|
||||||
if( c == 0 ){
|
if( c == 0 ){
|
||||||
Styler.setIconDefaultColor( this, btnToot, R.attr.ic_edit );
|
Styler.setIconDefaultColor( this, btnToot, R.attr.ic_edit );
|
||||||
Styler.setIconDefaultColor( this, btnMenu, R.attr.ic_hamburger );
|
Styler.setIconDefaultColor( this, btnMenu, R.attr.ic_hamburger );
|
||||||
|
Styler.setIconDefaultColor( this, btnQuickToot, R.attr.btn_post );
|
||||||
}else{
|
}else{
|
||||||
Styler.setIconCustomColor( this, btnToot, c, R.attr.ic_edit );
|
Styler.setIconCustomColor( this, btnToot, c, R.attr.ic_edit );
|
||||||
Styler.setIconCustomColor( this, btnMenu, c, R.attr.ic_hamburger );
|
Styler.setIconCustomColor( this, btnMenu, c, R.attr.ic_hamburger );
|
||||||
|
Styler.setIconCustomColor( this, btnQuickToot, c,R.attr.btn_post );
|
||||||
}
|
}
|
||||||
|
|
||||||
c = footer_tab_bg_color;
|
c = footer_tab_bg_color;
|
||||||
if( c == 0 ){
|
if( c == 0 ){
|
||||||
svColumnStrip.setBackgroundColor( Styler.getAttributeColor( this, R.attr.colorColumnStripBackground ) );
|
svColumnStrip.setBackgroundColor( Styler.getAttributeColor( this, R.attr.colorColumnStripBackground ) );
|
||||||
|
llQuickTootBar.setBackgroundColor( Styler.getAttributeColor( this, R.attr.colorColumnStripBackground ) );
|
||||||
}else{
|
}else{
|
||||||
svColumnStrip.setBackgroundColor( c );
|
svColumnStrip.setBackgroundColor( c );
|
||||||
|
svColumnStrip.setBackgroundColor( Styler.getAttributeColor( this, R.attr.colorColumnStripBackground ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
c = footer_tab_divider_color;
|
c = footer_tab_divider_color;
|
||||||
|
|
|
@ -25,10 +25,7 @@ import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -55,8 +52,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient;
|
import jp.juggler.subwaytooter.api.TootApiClient;
|
||||||
import jp.juggler.subwaytooter.api.TootApiResult;
|
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||||
|
@ -64,18 +59,16 @@ import jp.juggler.subwaytooter.api.entity.TootAttachment;
|
||||||
import jp.juggler.subwaytooter.api.entity.TootMention;
|
import jp.juggler.subwaytooter.api.entity.TootMention;
|
||||||
import jp.juggler.subwaytooter.api.entity.TootResults;
|
import jp.juggler.subwaytooter.api.entity.TootResults;
|
||||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm;
|
|
||||||
import jp.juggler.subwaytooter.dialog.DlgDraftPicker;
|
import jp.juggler.subwaytooter.dialog.DlgDraftPicker;
|
||||||
import jp.juggler.subwaytooter.table.AcctColor;
|
import jp.juggler.subwaytooter.table.AcctColor;
|
||||||
import jp.juggler.subwaytooter.table.AcctSet;
|
|
||||||
import jp.juggler.subwaytooter.table.PostDraft;
|
import jp.juggler.subwaytooter.table.PostDraft;
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog;
|
import jp.juggler.subwaytooter.dialog.ActionsDialog;
|
||||||
import jp.juggler.subwaytooter.table.TagSet;
|
|
||||||
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
import jp.juggler.subwaytooter.util.HTMLDecoder;
|
||||||
import jp.juggler.subwaytooter.util.LinkClickContext;
|
import jp.juggler.subwaytooter.util.LinkClickContext;
|
||||||
import jp.juggler.subwaytooter.util.LogCategory;
|
import jp.juggler.subwaytooter.util.LogCategory;
|
||||||
import jp.juggler.subwaytooter.util.MyClickableSpan;
|
import jp.juggler.subwaytooter.util.MyClickableSpan;
|
||||||
|
import jp.juggler.subwaytooter.util.PostHelper;
|
||||||
import jp.juggler.subwaytooter.view.MyEditText;
|
import jp.juggler.subwaytooter.view.MyEditText;
|
||||||
import jp.juggler.subwaytooter.view.MyNetworkImageView;
|
import jp.juggler.subwaytooter.view.MyNetworkImageView;
|
||||||
import jp.juggler.subwaytooter.util.PostAttachment;
|
import jp.juggler.subwaytooter.util.PostAttachment;
|
||||||
|
@ -162,7 +155,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.btnPost:
|
case R.id.btnPost:
|
||||||
performPost( false, false );
|
performPost();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.btnRemoveReply:
|
case R.id.btnRemoveReply:
|
||||||
|
@ -258,6 +251,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
ArrayList< PostAttachment > attachment_list;
|
ArrayList< PostAttachment > attachment_list;
|
||||||
AppState app_state;
|
AppState app_state;
|
||||||
boolean isPostComplete;
|
boolean isPostComplete;
|
||||||
|
PostHelper post_helper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate( @Nullable Bundle savedInstanceState ){
|
protected void onCreate( @Nullable Bundle savedInstanceState ){
|
||||||
|
@ -269,6 +263,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
|
|
||||||
initUI();
|
initUI();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if( account_list.isEmpty() ){
|
if( account_list.isEmpty() ){
|
||||||
Utils.showToast( this, true, R.string.please_add_account );
|
Utils.showToast( this, true, R.string.please_add_account );
|
||||||
finish();
|
finish();
|
||||||
|
@ -492,8 +488,9 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onDestroy(){
|
@Override protected void onDestroy(){
|
||||||
handler.removeCallbacks( proc_text_changed );
|
post_helper.onDestroy();
|
||||||
closeAcctPopup();
|
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -631,34 +628,14 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
etContent.addTextChangedListener( new TextWatcher() {
|
post_helper = new PostHelper( this, pref ,app_state.handler );
|
||||||
@Override
|
post_helper.attachEditText( formRoot,etContent,false, new PostHelper.Callback2() {
|
||||||
public void beforeTextChanged( CharSequence s, int start, int count, int after ){
|
@Override public void onTextUpdate(){
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged( CharSequence s, int start, int before, int count ){
|
|
||||||
handler.removeCallbacks( proc_text_changed );
|
|
||||||
handler.postDelayed( proc_text_changed, ( popup != null && popup.isShowing() ? 100L : 1000L ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged( Editable s ){
|
|
||||||
updateTextCount();
|
updateTextCount();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
etContent.setOnSelectionChangeListener( new MyEditText.OnSelectionChangeListener() {
|
|
||||||
|
|
||||||
@Override public void onSelectionChanged( int selStart, int selEnd ){
|
|
||||||
if( selStart != selEnd ){
|
|
||||||
// 範囲選択されてるならポップアップは閉じる
|
|
||||||
log.d( "onSelectionChanged: range selected" );
|
|
||||||
closeAcctPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
scrollView.getViewTreeObserver().addOnScrollChangedListener( scroll_listener );
|
scrollView.getViewTreeObserver().addOnScrollChangedListener( scroll_listener );
|
||||||
|
|
||||||
|
@ -668,116 +645,11 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
|
|
||||||
final ViewTreeObserver.OnScrollChangedListener scroll_listener = new ViewTreeObserver.OnScrollChangedListener() {
|
final ViewTreeObserver.OnScrollChangedListener scroll_listener = new ViewTreeObserver.OnScrollChangedListener() {
|
||||||
@Override public void onScrollChanged(){
|
@Override public void onScrollChanged(){
|
||||||
if( popup != null && popup.isShowing() ){
|
post_helper.onScrollChanged();
|
||||||
popup.updatePosition();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static final Pattern reCharsNotTag = Pattern.compile( "[\\s\\-+.,:;/]" );
|
|
||||||
|
|
||||||
final Runnable proc_text_changed = new Runnable() {
|
|
||||||
@Override public void run(){
|
|
||||||
int start = etContent.getSelectionStart();
|
|
||||||
int end = etContent.getSelectionEnd();
|
|
||||||
if( start != end ){
|
|
||||||
closeAcctPopup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String src = etContent.getText().toString();
|
|
||||||
int count_atMark = 0;
|
|
||||||
int[] pos_atMark = new int[ 2 ];
|
|
||||||
for( ; ; ){
|
|
||||||
if( count_atMark >= 2 ) break;
|
|
||||||
|
|
||||||
if( start == 0 ) break;
|
|
||||||
char c = src.charAt( start - 1 );
|
|
||||||
|
|
||||||
if( c == '@' ){
|
|
||||||
-- start;
|
|
||||||
pos_atMark[ count_atMark++ ] = start;
|
|
||||||
continue;
|
|
||||||
}else if( ( '0' <= c && c <= '9' )
|
|
||||||
|| ( 'A' <= c && c <= 'Z' )
|
|
||||||
|| ( 'a' <= c && c <= 'z' )
|
|
||||||
|| c == '_' || c == '-' || c == '.'
|
|
||||||
){
|
|
||||||
-- start;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// その他の文字種が出たら探索打ち切り
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// 登場した@の数
|
|
||||||
if( count_atMark == 0 ){
|
|
||||||
// 次はAcctじゃなくてHashtagの補完を試みる
|
|
||||||
checkTag();
|
|
||||||
return;
|
|
||||||
}else if( count_atMark == 1 ){
|
|
||||||
start = pos_atMark[ 0 ];
|
|
||||||
}else if( count_atMark == 2 ){
|
|
||||||
start = pos_atMark[ 1 ];
|
|
||||||
}
|
|
||||||
// 最低でも2文字ないと補完しない
|
|
||||||
if( end - start < 2 ){
|
|
||||||
closeAcctPopup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int limit = 100;
|
|
||||||
String s = src.substring( start, end );
|
|
||||||
ArrayList< String > acct_list = AcctSet.searchPrefix( s, limit );
|
|
||||||
log.d( "search for %s, result=%d", s, acct_list.size() );
|
|
||||||
if( acct_list.isEmpty() ){
|
|
||||||
closeAcctPopup();
|
|
||||||
}else{
|
|
||||||
if( popup == null || ! popup.isShowing() ){
|
|
||||||
popup = new PopupAutoCompleteAcct( ActPost.this, etContent, formRoot );
|
|
||||||
}
|
|
||||||
popup.setList( acct_list, start, end );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTag(){
|
|
||||||
int end = etContent.getSelectionEnd();
|
|
||||||
|
|
||||||
String src = etContent.getText().toString();
|
|
||||||
int last_sharp = src.lastIndexOf( '#', end - 1 );
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
private void closeAcctPopup(){
|
|
||||||
if( popup != null ){
|
|
||||||
popup.dismiss();
|
|
||||||
popup = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextCount(){
|
private void updateTextCount(){
|
||||||
String s = etContent.getText().toString();
|
String s = etContent.getText().toString();
|
||||||
int count_content = s.codePointCount( 0, s.length() );
|
int count_content = s.codePointCount( 0, s.length() );
|
||||||
|
@ -1474,219 +1346,34 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
|
||||||
///////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
// post
|
// post
|
||||||
|
|
||||||
// [:word:] 単語構成文字 (Letter | Mark | Decimal_Number | Connector_Punctuation)
|
private void performPost(){
|
||||||
// [:alpha:] 英字 (Letter | Mark)
|
post_helper.content = etContent.getText().toString().trim();
|
||||||
|
|
||||||
static final String word = "[_\\p{L}\\p{M}\\p{Nd}\\p{Pc}]";
|
|
||||||
static final String alpha = "[_\\p{L}\\p{M}]";
|
|
||||||
|
|
||||||
static final Pattern reTag = Pattern.compile(
|
|
||||||
"(?:^|[^/)\\w])#(" + word + "*" + alpha + word + "*)"
|
|
||||||
, Pattern.CASE_INSENSITIVE
|
|
||||||
);
|
|
||||||
|
|
||||||
private void performPost( final boolean bConfirmTag, final boolean bConfirmAccount ){
|
|
||||||
final String content = etContent.getText().toString().trim();
|
|
||||||
if( TextUtils.isEmpty( content ) ){
|
|
||||||
Utils.showToast( this, true, R.string.post_error_contents_empty );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String spoiler_text;
|
|
||||||
if( ! cbContentWarning.isChecked() ){
|
if( ! cbContentWarning.isChecked() ){
|
||||||
spoiler_text = null;
|
post_helper.spoiler_text = null;
|
||||||
}else{
|
}else{
|
||||||
spoiler_text = etContentWarning.getText().toString().trim();
|
post_helper.spoiler_text = etContentWarning.getText().toString().trim();
|
||||||
if( TextUtils.isEmpty( spoiler_text ) ){
|
|
||||||
Utils.showToast( this, true, R.string.post_error_contents_warning_empty );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ! bConfirmAccount ){
|
post_helper.visibility = this.visibility;
|
||||||
DlgConfirm.open( this
|
post_helper.bNSFW = cbNSFW.isChecked();
|
||||||
, getString( R.string.confirm_post_from, AcctColor.getNickname( account.acct ) )
|
|
||||||
, new DlgConfirm.Callback() {
|
|
||||||
@Override public boolean isConfirmEnabled(){
|
|
||||||
return account.confirm_post;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void setConfirmEnabled( boolean bv ){
|
post_helper.in_reply_to_id = this.in_reply_to_id;
|
||||||
account.confirm_post = bv;
|
|
||||||
account.saveSetting();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void onOK(){
|
post_helper.attachment_list = this.attachment_list;
|
||||||
performPost( bConfirmTag, true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ! bConfirmTag ){
|
post_helper.post( account, false, false, new PostHelper.Callback() {
|
||||||
Matcher m = reTag.matcher( content );
|
|
||||||
if( m.find() && ! TootStatus.VISIBILITY_PUBLIC.equals( visibility ) ){
|
|
||||||
new AlertDialog.Builder( this )
|
|
||||||
.setCancelable( true )
|
|
||||||
.setMessage( R.string.hashtag_and_visibility_not_match )
|
|
||||||
.setNegativeButton( R.string.cancel, null )
|
|
||||||
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override public void onClick( DialogInterface dialog, int which ){
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
performPost( true, bConfirmAccount );
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
@Override public void onPostComplete( SavedAccount target_account, TootStatus status ){
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.putExtra( EXTRA_POSTED_ACCT, target_account.acct );
|
||||||
|
data.putExtra( EXTRA_POSTED_STATUS_ID, status.id );
|
||||||
|
|
||||||
sb.append( "status=" );
|
setResult( RESULT_OK, data );
|
||||||
sb.append( Uri.encode( content ) );
|
isPostComplete = true;
|
||||||
|
ActPost.this.finish();
|
||||||
sb.append( "&visibility=" );
|
|
||||||
sb.append( Uri.encode( visibility ) );
|
|
||||||
|
|
||||||
if( cbNSFW.isChecked() ){
|
|
||||||
sb.append( "&sensitive=1" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( spoiler_text != null ){
|
|
||||||
sb.append( "&spoiler_text=" );
|
|
||||||
sb.append( Uri.encode( spoiler_text ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( in_reply_to_id != - 1L ){
|
|
||||||
sb.append( "&in_reply_to_id=" );
|
|
||||||
sb.append( Long.toString( in_reply_to_id ) );
|
|
||||||
}
|
|
||||||
if( attachment_list != null ){
|
|
||||||
for( PostAttachment pa : attachment_list ){
|
|
||||||
if( pa.attachment != null ){
|
|
||||||
sb.append( "&media_ids[]=" ).append( pa.attachment.id );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ProgressDialog progress = new ProgressDialog( this );
|
|
||||||
|
|
||||||
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
|
||||||
final SavedAccount target_account = account;
|
|
||||||
|
|
||||||
TootStatus status;
|
|
||||||
|
|
||||||
@Override protected TootApiResult doInBackground( Void... params ){
|
|
||||||
TootApiClient client = new TootApiClient( ActPost.this, new TootApiClient.Callback() {
|
|
||||||
@Override public boolean isApiCancelled(){
|
|
||||||
return isCancelled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void publishApiProgress( final String s ){
|
|
||||||
Utils.runOnMainThread( new Runnable() {
|
|
||||||
@Override public void run(){
|
|
||||||
progress.setMessage( s );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
client.setAccount( target_account );
|
|
||||||
String post_content = sb.toString();
|
|
||||||
String digest = Utils.digestSHA256( post_content + target_account.acct );
|
|
||||||
|
|
||||||
Request.Builder request_builder = new Request.Builder()
|
|
||||||
.post( RequestBody.create(
|
|
||||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
|
|
||||||
, post_content
|
|
||||||
) );
|
|
||||||
|
|
||||||
if( ! pref.getBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, false ) ){
|
|
||||||
request_builder.header( "Idempotency-Key", digest );
|
|
||||||
}
|
|
||||||
|
|
||||||
TootApiResult result = client.request( "/api/v1/statuses", request_builder );
|
|
||||||
if( result != null && result.object != null ){
|
|
||||||
status = TootStatus.parse( log, account, account.host, 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCancelled(){
|
|
||||||
onPostExecute( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute( TootApiResult result ){
|
|
||||||
try{
|
|
||||||
progress.dismiss();
|
|
||||||
}catch( Throwable ignored ){
|
|
||||||
// java.lang.IllegalArgumentException:
|
|
||||||
// at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:396)
|
|
||||||
// at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:322)
|
|
||||||
// at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:116)
|
|
||||||
// at android.app.Dialog.dismissDialog(Dialog.java:341)
|
|
||||||
// at android.app.Dialog.dismiss(Dialog.java:324)
|
|
||||||
// at jp.juggler.subwaytooter.ActMain$10$1.onPostExecute(ActMain.java:867)
|
|
||||||
// at jp.juggler.subwaytooter.ActMain$10$1.onPostExecute(ActMain.java:837)
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection StatementWithEmptyBody
|
|
||||||
if( result == null ){
|
|
||||||
// cancelled.
|
|
||||||
}else if( status != null ){
|
|
||||||
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
|
||||||
Intent data = new Intent();
|
|
||||||
data.putExtra( EXTRA_POSTED_ACCT, target_account.acct );
|
|
||||||
data.putExtra( EXTRA_POSTED_STATUS_ID, status.id );
|
|
||||||
|
|
||||||
setResult( RESULT_OK, data );
|
|
||||||
isPostComplete = true;
|
|
||||||
ActPost.this.finish();
|
|
||||||
}else{
|
|
||||||
Utils.showToast( ActPost.this, true, result.error );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
progress.setIndeterminate( true );
|
|
||||||
progress.setCancelable( true );
|
|
||||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel( DialogInterface dialog ){
|
|
||||||
task.cancel( true );
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
progress.show();
|
|
||||||
task.executeOnExecutor( App1.task_executor );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
|
@ -298,6 +298,7 @@ public class AppDataExporter {
|
||||||
case Pref.KEY_PRIOR_CHROME:
|
case Pref.KEY_PRIOR_CHROME:
|
||||||
case Pref.KEY_POST_BUTTON_BAR_AT_TOP:
|
case Pref.KEY_POST_BUTTON_BAR_AT_TOP:
|
||||||
case Pref.KEY_DONT_DUPLICATION_CHECK:
|
case Pref.KEY_DONT_DUPLICATION_CHECK:
|
||||||
|
case Pref.KEY_QUICK_TOOT_BAR:
|
||||||
boolean bv = reader.nextBoolean();
|
boolean bv = reader.nextBoolean();
|
||||||
e.putBoolean( k, bv );
|
e.putBoolean( k, bv );
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -67,6 +67,8 @@ public class Pref {
|
||||||
public static final String KEY_ACCT_FONT_SIZE = "acct_font_size";
|
public static final String KEY_ACCT_FONT_SIZE = "acct_font_size";
|
||||||
|
|
||||||
public static final String KEY_DONT_DUPLICATION_CHECK = "dont_duplication_check";
|
public static final String KEY_DONT_DUPLICATION_CHECK = "dont_duplication_check";
|
||||||
|
public static final String KEY_QUICK_TOOT_BAR = "quick_toot_bar";
|
||||||
|
|
||||||
|
|
||||||
// 項目を追加したらAppDataExporter#importPref のswitch文も更新すること
|
// 項目を追加したらAppDataExporter#importPref のswitch文も更新すること
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package jp.juggler.subwaytooter;
|
package jp.juggler.subwaytooter.util;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
@ -6,7 +6,6 @@ import android.support.v4.content.ContextCompat;
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.CheckedTextView;
|
import android.widget.CheckedTextView;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
@ -14,6 +13,9 @@ import android.widget.PopupWindow;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import jp.juggler.subwaytooter.R;
|
||||||
|
import jp.juggler.subwaytooter.Styler;
|
||||||
|
|
||||||
class PopupAutoCompleteAcct {
|
class PopupAutoCompleteAcct {
|
||||||
final Activity activity;
|
final Activity activity;
|
||||||
private final EditText etContent;
|
private final EditText etContent;
|
||||||
|
@ -25,6 +27,8 @@ class PopupAutoCompleteAcct {
|
||||||
|
|
||||||
private int popup_rows;
|
private int popup_rows;
|
||||||
|
|
||||||
|
private boolean bMainScreen;
|
||||||
|
|
||||||
void dismiss(){
|
void dismiss(){
|
||||||
acct_popup.dismiss();
|
acct_popup.dismiss();
|
||||||
}
|
}
|
||||||
|
@ -33,13 +37,14 @@ class PopupAutoCompleteAcct {
|
||||||
return acct_popup.isShowing();
|
return acct_popup.isShowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupAutoCompleteAcct( Activity activity, EditText etContent, View formRoot ){
|
PopupAutoCompleteAcct( Activity activity, EditText etContent, View formRoot, boolean bMainScreen ){
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.etContent = etContent;
|
this.etContent = etContent;
|
||||||
this.formRoot = formRoot;
|
this.formRoot = formRoot;
|
||||||
|
this.bMainScreen = bMainScreen;
|
||||||
this.density = activity.getResources().getDisplayMetrics().density;
|
this.density = activity.getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
popup_width = (int)(0.5f +240f * density );
|
popup_width = (int) ( 0.5f + 240f * density );
|
||||||
|
|
||||||
@SuppressLint("InflateParams") View viewRoot =
|
@SuppressLint("InflateParams") View viewRoot =
|
||||||
activity.getLayoutInflater().inflate( R.layout.acct_complete_popup, null, false );
|
activity.getLayoutInflater().inflate( R.layout.acct_complete_popup, null, false );
|
||||||
|
@ -102,26 +107,40 @@ class PopupAutoCompleteAcct {
|
||||||
etContent.getLocationOnScreen( location );
|
etContent.getLocationOnScreen( location );
|
||||||
int text_top = location[ 1 ];
|
int text_top = location[ 1 ];
|
||||||
|
|
||||||
formRoot.getLocationOnScreen( location );
|
int popup_top;
|
||||||
int form_top = location[ 1 ];
|
int popup_height;
|
||||||
int form_bottom = location[ 1 ] + formRoot.getHeight();
|
|
||||||
|
|
||||||
Layout layout = etContent.getLayout();
|
if( bMainScreen ){
|
||||||
|
int popup_bottom = text_top + etContent.getTotalPaddingTop() - etContent.getScrollY();
|
||||||
|
int max = popup_bottom-(int) ( 0.5f + 48f * 1f * density );
|
||||||
|
int min = (int) ( 0.5f + 48f * 2f * density );
|
||||||
|
popup_height = (int) ( 0.5f + 48f * popup_rows * density );
|
||||||
|
if( popup_height < min ) popup_height = min;
|
||||||
|
if( popup_height > max ) popup_height = max;
|
||||||
|
popup_top = popup_bottom - popup_height;
|
||||||
|
|
||||||
int popup_top = text_top
|
}else{
|
||||||
+ etContent.getTotalPaddingTop()
|
formRoot.getLocationOnScreen( location );
|
||||||
+ layout.getLineBottom( layout.getLineCount() - 1 )
|
int form_top = location[ 1 ];
|
||||||
- etContent.getScrollY();
|
int form_bottom = location[ 1 ] + formRoot.getHeight();
|
||||||
|
|
||||||
if( popup_top < form_top ) popup_top = form_top;
|
Layout layout = etContent.getLayout();
|
||||||
|
|
||||||
int popup_height = form_bottom - popup_top;
|
popup_top = text_top
|
||||||
|
+ etContent.getTotalPaddingTop()
|
||||||
|
+ layout.getLineBottom( layout.getLineCount() - 1 )
|
||||||
|
- etContent.getScrollY();
|
||||||
|
|
||||||
int min = (int) ( 0.5f + 48f * 2f * density );
|
if( popup_top < form_top ) popup_top = form_top;
|
||||||
if( popup_height < min ) popup_height = min;
|
|
||||||
|
|
||||||
int max = (int) ( 0.5f + 48f * popup_rows * density );
|
popup_height = form_bottom - popup_top;
|
||||||
if( popup_height > max ) popup_height = max;
|
|
||||||
|
int min = (int) ( 0.5f + 48f * 2f * density );
|
||||||
|
int max = (int) ( 0.5f + 48f * popup_rows * density );
|
||||||
|
|
||||||
|
if( popup_height < min ) popup_height = min;
|
||||||
|
if( popup_height > max ) popup_height = max;
|
||||||
|
}
|
||||||
|
|
||||||
if( acct_popup.isShowing() ){
|
if( acct_popup.isShowing() ){
|
||||||
acct_popup.update( 0, popup_top, popup_width, popup_height );
|
acct_popup.update( 0, popup_top, popup_width, popup_height );
|
|
@ -0,0 +1,433 @@
|
||||||
|
package jp.juggler.subwaytooter.util;
|
||||||
|
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Handler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import jp.juggler.subwaytooter.App1;
|
||||||
|
import jp.juggler.subwaytooter.Pref;
|
||||||
|
import jp.juggler.subwaytooter.R;
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiClient;
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||||
|
import jp.juggler.subwaytooter.dialog.DlgConfirm;
|
||||||
|
import jp.juggler.subwaytooter.table.AcctColor;
|
||||||
|
import jp.juggler.subwaytooter.table.AcctSet;
|
||||||
|
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||||
|
import jp.juggler.subwaytooter.table.TagSet;
|
||||||
|
import jp.juggler.subwaytooter.view.MyEditText;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
|
||||||
|
|
||||||
|
public class PostHelper {
|
||||||
|
private static final LogCategory log = new LogCategory( "PostHelper" );
|
||||||
|
|
||||||
|
|
||||||
|
public interface Callback{
|
||||||
|
void onPostComplete(SavedAccount target_account,TootStatus status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AppCompatActivity activity;
|
||||||
|
private final SharedPreferences pref;
|
||||||
|
private final Handler handler;
|
||||||
|
|
||||||
|
public PostHelper( AppCompatActivity activity ,SharedPreferences pref,Handler handler ){
|
||||||
|
this.activity = activity;
|
||||||
|
this.pref = pref;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [:word:] 単語構成文字 (Letter | Mark | Decimal_Number | Connector_Punctuation)
|
||||||
|
// [:alpha:] 英字 (Letter | Mark)
|
||||||
|
|
||||||
|
private static final String word = "[_\\p{L}\\p{M}\\p{Nd}\\p{Pc}]";
|
||||||
|
private static final String alpha = "[_\\p{L}\\p{M}]";
|
||||||
|
|
||||||
|
private static final Pattern reTag = Pattern.compile(
|
||||||
|
"(?:^|[^/)\\w])#(" + word + "*" + alpha + word + "*)"
|
||||||
|
, Pattern.CASE_INSENSITIVE
|
||||||
|
);
|
||||||
|
|
||||||
|
public String content;
|
||||||
|
public String spoiler_text;
|
||||||
|
public String visibility;
|
||||||
|
public boolean bNSFW;
|
||||||
|
public long in_reply_to_id;
|
||||||
|
public ArrayList< PostAttachment > attachment_list;
|
||||||
|
|
||||||
|
public void post( final SavedAccount account,final boolean bConfirmTag, final boolean bConfirmAccount ,final Callback callback){
|
||||||
|
if( TextUtils.isEmpty( content ) ){
|
||||||
|
Utils.showToast( activity, true, R.string.post_error_contents_empty );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( spoiler_text != null && spoiler_text.isEmpty() ){
|
||||||
|
Utils.showToast( activity, true, R.string.post_error_contents_warning_empty );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! bConfirmAccount ){
|
||||||
|
DlgConfirm.open( activity
|
||||||
|
, activity.getString( R.string.confirm_post_from, AcctColor.getNickname( account.acct ) )
|
||||||
|
, new DlgConfirm.Callback() {
|
||||||
|
@Override public boolean isConfirmEnabled(){
|
||||||
|
return account.confirm_post;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void setConfirmEnabled( boolean bv ){
|
||||||
|
account.confirm_post = bv;
|
||||||
|
account.saveSetting();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onOK(){
|
||||||
|
post( account,bConfirmTag, true ,callback);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! bConfirmTag ){
|
||||||
|
Matcher m = reTag.matcher( content );
|
||||||
|
if( m.find() && ! TootStatus.VISIBILITY_PUBLIC.equals( visibility ) ){
|
||||||
|
new AlertDialog.Builder( activity )
|
||||||
|
.setCancelable( true )
|
||||||
|
.setMessage( R.string.hashtag_and_visibility_not_match )
|
||||||
|
.setNegativeButton( R.string.cancel, null )
|
||||||
|
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override public void onClick( DialogInterface dialog, int which ){
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
post( account,true, bConfirmAccount ,callback);
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append( "status=" );
|
||||||
|
sb.append( Uri.encode( content ) );
|
||||||
|
|
||||||
|
sb.append( "&visibility=" );
|
||||||
|
sb.append( Uri.encode( visibility ) );
|
||||||
|
|
||||||
|
if( bNSFW ){
|
||||||
|
sb.append( "&sensitive=1" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( spoiler_text != null ){
|
||||||
|
sb.append( "&spoiler_text=" );
|
||||||
|
sb.append( Uri.encode( spoiler_text ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( in_reply_to_id != - 1L ){
|
||||||
|
sb.append( "&in_reply_to_id=" );
|
||||||
|
sb.append( Long.toString( in_reply_to_id ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( attachment_list != null ){
|
||||||
|
for( PostAttachment pa : attachment_list ){
|
||||||
|
if( pa.attachment != null ){
|
||||||
|
sb.append( "&media_ids[]=" ).append( pa.attachment.id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProgressDialog progress = new ProgressDialog( activity );
|
||||||
|
|
||||||
|
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
||||||
|
final SavedAccount target_account = account;
|
||||||
|
|
||||||
|
TootStatus status;
|
||||||
|
|
||||||
|
@Override protected TootApiResult doInBackground( Void... params ){
|
||||||
|
TootApiClient client = new TootApiClient( activity, new TootApiClient.Callback() {
|
||||||
|
@Override public boolean isApiCancelled(){
|
||||||
|
return isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void publishApiProgress( final String s ){
|
||||||
|
Utils.runOnMainThread( new Runnable() {
|
||||||
|
@Override public void run(){
|
||||||
|
progress.setMessage( s );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
client.setAccount( target_account );
|
||||||
|
String post_content = sb.toString();
|
||||||
|
String digest = Utils.digestSHA256( post_content + target_account.acct );
|
||||||
|
|
||||||
|
Request.Builder request_builder = new Request.Builder()
|
||||||
|
.post( RequestBody.create(
|
||||||
|
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
|
||||||
|
, post_content
|
||||||
|
) );
|
||||||
|
|
||||||
|
if( ! pref.getBoolean( Pref.KEY_DONT_DUPLICATION_CHECK, false ) ){
|
||||||
|
request_builder.header( "Idempotency-Key", digest );
|
||||||
|
}
|
||||||
|
|
||||||
|
TootApiResult result = client.request( "/api/v1/statuses", request_builder );
|
||||||
|
if( result != null && result.object != null ){
|
||||||
|
status = TootStatus.parse( log, account, account.host, 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled(){
|
||||||
|
onPostExecute( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute( TootApiResult result ){
|
||||||
|
try{
|
||||||
|
progress.dismiss();
|
||||||
|
}catch( Throwable ignored ){
|
||||||
|
// java.lang.IllegalArgumentException:
|
||||||
|
// at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:396)
|
||||||
|
// at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:322)
|
||||||
|
// at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:116)
|
||||||
|
// at android.app.Dialog.dismissDialog(Dialog.java:341)
|
||||||
|
// at android.app.Dialog.dismiss(Dialog.java:324)
|
||||||
|
// at jp.juggler.subwaytooter.ActMain$10$1.onPostExecute(ActMain.java:867)
|
||||||
|
// at jp.juggler.subwaytooter.ActMain$10$1.onPostExecute(ActMain.java:837)
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if( result == null ){
|
||||||
|
// cancelled.
|
||||||
|
}else if( status != null ){
|
||||||
|
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
||||||
|
callback.onPostComplete( target_account,status );
|
||||||
|
}else{
|
||||||
|
Utils.showToast( activity, true, result.error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
progress.setIndeterminate( true );
|
||||||
|
progress.setCancelable( true );
|
||||||
|
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel( DialogInterface dialog ){
|
||||||
|
task.cancel( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
progress.show();
|
||||||
|
task.executeOnExecutor( App1.task_executor );
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback2{
|
||||||
|
void onTextUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Callback2 callback2;
|
||||||
|
|
||||||
|
private MyEditText et;
|
||||||
|
private PopupAutoCompleteAcct popup;
|
||||||
|
private View formRoot;
|
||||||
|
private boolean bMainScreen;
|
||||||
|
|
||||||
|
public void closeAcctPopup(){
|
||||||
|
if( popup != null ){
|
||||||
|
popup.dismiss();
|
||||||
|
popup = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onScrollChanged(){
|
||||||
|
if( popup != null && popup.isShowing() ){
|
||||||
|
popup.updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroy(){
|
||||||
|
handler.removeCallbacks( proc_text_changed );
|
||||||
|
closeAcctPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void attachEditText( View _formRoot, MyEditText _et, boolean bMainScreen, Callback2 _callback2){
|
||||||
|
this.formRoot = _formRoot;
|
||||||
|
this.et = _et;
|
||||||
|
this.callback2 = _callback2;
|
||||||
|
this.bMainScreen = bMainScreen;
|
||||||
|
|
||||||
|
et.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 ){
|
||||||
|
handler.removeCallbacks( proc_text_changed );
|
||||||
|
handler.postDelayed( proc_text_changed, ( popup != null && popup.isShowing() ? 100L : 1000L ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged( Editable s ){
|
||||||
|
callback2.onTextUpdate();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
et.setOnSelectionChangeListener( new MyEditText.OnSelectionChangeListener() {
|
||||||
|
@Override public void onSelectionChanged( int selStart, int selEnd ){
|
||||||
|
if( selStart != selEnd ){
|
||||||
|
// 範囲選択されてるならポップアップは閉じる
|
||||||
|
log.d( "onSelectionChanged: range selected" );
|
||||||
|
closeAcctPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static final Pattern reCharsNotTag = Pattern.compile( "[\\s\\-+.,:;/]" );
|
||||||
|
|
||||||
|
private final Runnable proc_text_changed = new Runnable() {
|
||||||
|
@Override public void run(){
|
||||||
|
int start = et.getSelectionStart();
|
||||||
|
int end = et.getSelectionEnd();
|
||||||
|
if( start != end ){
|
||||||
|
closeAcctPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String src = et.getText().toString();
|
||||||
|
int count_atMark = 0;
|
||||||
|
int[] pos_atMark = new int[ 2 ];
|
||||||
|
for( ; ; ){
|
||||||
|
if( count_atMark >= 2 ) break;
|
||||||
|
|
||||||
|
if( start == 0 ) break;
|
||||||
|
char c = src.charAt( start - 1 );
|
||||||
|
|
||||||
|
if( c == '@' ){
|
||||||
|
-- start;
|
||||||
|
pos_atMark[ count_atMark++ ] = start;
|
||||||
|
continue;
|
||||||
|
}else if( ( '0' <= c && c <= '9' )
|
||||||
|
|| ( 'A' <= c && c <= 'Z' )
|
||||||
|
|| ( 'a' <= c && c <= 'z' )
|
||||||
|
|| c == '_' || c == '-' || c == '.'
|
||||||
|
){
|
||||||
|
-- start;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// その他の文字種が出たら探索打ち切り
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 登場した@の数
|
||||||
|
if( count_atMark == 0 ){
|
||||||
|
// 次はAcctじゃなくてHashtagの補完を試みる
|
||||||
|
checkTag();
|
||||||
|
return;
|
||||||
|
}else if( count_atMark == 1 ){
|
||||||
|
start = pos_atMark[ 0 ];
|
||||||
|
}else if( count_atMark == 2 ){
|
||||||
|
start = pos_atMark[ 1 ];
|
||||||
|
}
|
||||||
|
// 最低でも2文字ないと補完しない
|
||||||
|
if( end - start < 2 ){
|
||||||
|
closeAcctPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int limit = 100;
|
||||||
|
String s = src.substring( start, end );
|
||||||
|
ArrayList< String > acct_list = AcctSet.searchPrefix( s, limit );
|
||||||
|
log.d( "search for %s, result=%d", s, acct_list.size() );
|
||||||
|
if( acct_list.isEmpty() ){
|
||||||
|
closeAcctPopup();
|
||||||
|
}else{
|
||||||
|
if( popup == null || ! popup.isShowing() ){
|
||||||
|
popup = new PopupAutoCompleteAcct( activity, et, formRoot,bMainScreen );
|
||||||
|
}
|
||||||
|
popup.setList( acct_list, start, end );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkTag(){
|
||||||
|
int end = et.getSelectionEnd();
|
||||||
|
|
||||||
|
String src = et.getText().toString();
|
||||||
|
int last_sharp = src.lastIndexOf( '#', end - 1 );
|
||||||
|
|
||||||
|
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( activity, et, formRoot ,bMainScreen);
|
||||||
|
}
|
||||||
|
popup.setList( tag_list, last_sharp, end );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -287,6 +287,8 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<View style="@style/setting_divider"/>
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -304,6 +306,22 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:text="@string/show_quick_toot_bar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/swQuickTootBar"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:gravity="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<View style="@style/setting_divider"/>
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/llFormRoot"
|
||||||
>
|
>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
@ -46,10 +47,12 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<jp.juggler.subwaytooter.view.MyRecyclerView
|
<jp.juggler.subwaytooter.view.MyRecyclerView
|
||||||
android:id="@+id/rvPager"
|
android:id="@+id/rvPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -63,28 +66,28 @@
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:background="@drawable/btn_bg_ddd"
|
android:background="@drawable/btn_bg_ddd"
|
||||||
app:srcCompat="?attr/ic_hamburger"
|
|
||||||
android:contentDescription="@string/menu"
|
android:contentDescription="@string/menu"
|
||||||
|
app:srcCompat="?attr/ic_hamburger"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
android:id="@+id/vFooterDivider1"
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorImageButton"
|
android:background="?attr/colorImageButton"
|
||||||
android:id="@+id/vFooterDivider1"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
|
android:id="@+id/svColumnStrip"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/colorColumnStripBackground"
|
||||||
android:cacheColorHint="#00000000"
|
android:cacheColorHint="#00000000"
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
android:fadingEdgeLength="20dp"
|
android:fadingEdgeLength="20dp"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
android:background="?attr/colorColumnStripBackground"
|
|
||||||
android:id="@+id/svColumnStrip"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<jp.juggler.subwaytooter.view.ColumnStripLinearLayout
|
<jp.juggler.subwaytooter.view.ColumnStripLinearLayout
|
||||||
|
@ -96,23 +99,53 @@
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
android:id="@+id/vFooterDivider2"
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorImageButton"
|
android:background="?attr/colorImageButton"
|
||||||
android:id="@+id/vFooterDivider2"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnToot"
|
android:id="@+id/btnToot"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:background="@drawable/btn_bg_ddd"
|
android:background="@drawable/btn_bg_ddd"
|
||||||
app:srcCompat="?attr/ic_edit"
|
|
||||||
android:contentDescription="@string/toot"
|
android:contentDescription="@string/toot"
|
||||||
|
app:srcCompat="?attr/ic_edit"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llQuickTootBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
>
|
||||||
|
|
||||||
|
<jp.juggler.subwaytooter.view.MyEditText
|
||||||
|
android:id="@+id/etQuickToot"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:hint="@string/quick_toot_hint"
|
||||||
|
android:imeOptions="actionSend"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnQuickToot"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/btn_bg_ddd"
|
||||||
|
android:contentDescription="@string/post"
|
||||||
|
app:srcCompat="?attr/btn_post"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<!--<android.support.design.widget.FloatingActionButton-->
|
<!--<android.support.design.widget.FloatingActionButton-->
|
||||||
<!--android:id="@+id/"-->
|
<!--android:id="@+id/"-->
|
||||||
<!--android:layout_width="wrap_content"-->
|
<!--android:layout_width="wrap_content"-->
|
||||||
|
@ -133,7 +166,6 @@
|
||||||
<!--app:srcCompat="?attr/ic_menu"-->
|
<!--app:srcCompat="?attr/ic_menu"-->
|
||||||
<!--/>-->
|
<!--/>-->
|
||||||
|
|
||||||
|
|
||||||
<!--<android.support.design.widget.CoordinatorLayout-->
|
<!--<android.support.design.widget.CoordinatorLayout-->
|
||||||
<!--android:layout_width="match_parent"-->
|
<!--android:layout_width="match_parent"-->
|
||||||
<!--android:layout_height="match_parent"-->
|
<!--android:layout_height="match_parent"-->
|
||||||
|
@ -148,6 +180,7 @@
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
|
|
||||||
app:menu="@menu/menu_navi_drawer"/>
|
app:menu="@menu/menu_navi_drawer"
|
||||||
|
/>
|
||||||
<!-- app:headerLayout="@layout/nav_header_act_main" -->
|
<!-- app:headerLayout="@layout/nav_header_act_main" -->
|
||||||
</android.support.v4.widget.DrawerLayout>
|
</android.support.v4.widget.DrawerLayout>
|
||||||
|
|
|
@ -388,6 +388,8 @@
|
||||||
<string name="timeline_font_size">Timeline font size (unit:sp. leave empty to default. app restart required)</string>
|
<string name="timeline_font_size">Timeline font size (unit:sp. leave empty to default. app restart required)</string>
|
||||||
<string name="acct_font_size">Acct font size (unit:sp. leave empty to default. app restart required)</string>
|
<string name="acct_font_size">Acct font size (unit:sp. leave empty to default. app restart required)</string>
|
||||||
<string name="dont_add_duplication_check_header">Don\'t add duplication check header</string>
|
<string name="dont_add_duplication_check_header">Don\'t add duplication check header</string>
|
||||||
|
<string name="show_quick_toot_bar">Show \"Quick Toot\" bar (app restart required.)</string>
|
||||||
|
<string name="quick_toot_hint">Quick Toot</string>
|
||||||
|
|
||||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</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>-->
|
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||||
|
|
|
@ -675,5 +675,7 @@
|
||||||
<string name="timeline_font_size">タイムラインのフォントサイズ(単位:sp。空欄でデフォルト。アプリ再起動が必要)</string>
|
<string name="timeline_font_size">タイムラインのフォントサイズ(単位:sp。空欄でデフォルト。アプリ再起動が必要)</string>
|
||||||
<string name="acct_font_size">Acctのフォントサイズ(単位:sp。空欄でデフォルト。アプリ再起動が必要)</string>
|
<string name="acct_font_size">Acctのフォントサイズ(単位:sp。空欄でデフォルト。アプリ再起動が必要)</string>
|
||||||
<string name="dont_add_duplication_check_header">重複チェックヘッダを付与しない</string>
|
<string name="dont_add_duplication_check_header">重複チェックヘッダを付与しない</string>
|
||||||
|
<string name="quick_toot_hint">簡易投稿入力</string>
|
||||||
|
<string name="show_quick_toot_bar">簡易投稿バーを表示(アプリ再起動が必要)</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -383,5 +383,7 @@
|
||||||
<string name="timeline_font_size">Timeline font size (unit:sp. leave empty to default. app restart required)</string>
|
<string name="timeline_font_size">Timeline font size (unit:sp. leave empty to default. app restart required)</string>
|
||||||
<string name="acct_font_size">Acct font size (unit:sp. leave empty to default. app restart required)</string>
|
<string name="acct_font_size">Acct font size (unit:sp. leave empty to default. app restart required)</string>
|
||||||
<string name="dont_add_duplication_check_header">Don\'t add duplication check header</string>
|
<string name="dont_add_duplication_check_header">Don\'t add duplication check header</string>
|
||||||
|
<string name="show_quick_toot_bar">Show \"Quick Toot\" bar (app restart required.)</string>
|
||||||
|
<string name="quick_toot_hint">Quick Toot</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue