This commit is contained in:
tateisu 2017-07-31 13:30:45 +09:00
parent 9bc25e842e
commit 74b2f1173a
14 changed files with 559 additions and 158 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 25
versionCode 109
versionName "1.0.9"
versionCode 111
versionName "1.1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

View File

@ -1,6 +1,7 @@
package jp.juggler.subwaytooter;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
@ -23,6 +24,7 @@ import android.widget.TextView;
import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.dialog.DlgAccessToken;
import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.LogCategory;
@ -93,6 +95,7 @@ public class ActAccountSetting extends AppCompatActivity
TextView tvInstance;
TextView tvUser;
View btnAccessToken;
View btnInputAccessToken;
View btnAccountRemove;
Button btnVisibility;
Switch swNSFWOpen;
@ -125,6 +128,7 @@ public class ActAccountSetting extends AppCompatActivity
tvInstance = (TextView) findViewById( R.id.tvInstance );
tvUser = (TextView) findViewById( R.id.tvUser );
btnAccessToken = findViewById( R.id.btnAccessToken );
btnInputAccessToken = findViewById( R.id.btnInputAccessToken );
btnAccountRemove = findViewById( R.id.btnAccountRemove );
btnVisibility = (Button) findViewById( R.id.btnVisibility );
swNSFWOpen = (Switch) findViewById( R.id.swNSFWOpen );
@ -145,6 +149,7 @@ public class ActAccountSetting extends AppCompatActivity
btnOpenBrowser.setOnClickListener( this );
btnAccessToken.setOnClickListener( this );
btnInputAccessToken.setOnClickListener( this );
btnAccountRemove.setOnClickListener( this );
btnVisibility.setOnClickListener( this );
btnUserCustom.setOnClickListener( this );
@ -200,6 +205,7 @@ public class ActAccountSetting extends AppCompatActivity
boolean enabled = ! a.isPseudo();
btnAccessToken.setEnabled( enabled );
btnInputAccessToken.setEnabled( enabled );
btnVisibility.setEnabled( enabled );
btnVisibility.setEnabled( enabled );
btnVisibility.setEnabled( enabled );
@ -258,6 +264,10 @@ public class ActAccountSetting extends AppCompatActivity
case R.id.btnAccessToken:
performAccessToken();
break;
case R.id.btnInputAccessToken:
inputAccessToken();
break;
case R.id.btnAccountRemove:
performAccountRemove();
break;
@ -420,8 +430,6 @@ public class ActAccountSetting extends AppCompatActivity
final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
long row_id;
@Override
protected TootApiResult doInBackground( Void... params ){
TootApiClient api_client = new TootApiClient( ActAccountSetting.this, new TootApiClient.Callback() {
@ -481,6 +489,16 @@ public class ActAccountSetting extends AppCompatActivity
task.executeOnExecutor( App1.task_executor );
}
static final int RESULT_INPUT_ACCESS_TOKEN = RESULT_FIRST_USER + 10;
static final String EXTRA_DB_ID = "db_id";
private void inputAccessToken(){
Intent data = new Intent();
data.putExtra( EXTRA_DB_ID,account.db_id );
setResult( RESULT_INPUT_ACCESS_TOKEN, data );
finish();
}
private void openNotificationSoundPicker(){
Intent intent = new Intent( RingtoneManager.ACTION_RINGTONE_PICKER );
intent.putExtra( RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION );

View File

@ -71,6 +71,7 @@ import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.api.entity.TootStatusLike;
import jp.juggler.subwaytooter.api_msp.entity.MSPToot;
import jp.juggler.subwaytooter.dialog.AccountPicker;
import jp.juggler.subwaytooter.dialog.DlgAccessToken;
import jp.juggler.subwaytooter.dialog.DlgConfirm;
import jp.juggler.subwaytooter.dialog.LoginForm;
import jp.juggler.subwaytooter.dialog.ReportForm;
@ -460,19 +461,6 @@ public class ActMain extends AppCompatActivity
}
}
}else if( requestCode == REQUEST_CODE_ACCOUNT_SETTING ){
updateColumnStrip();
for( Column column : app_state.column_list ){
column.fireShowColumnHeader();
}
if( data != null ){
startAccessTokenUpdate( data );
return;
}
}else if( requestCode == REQUEST_APP_ABOUT ){
if( data != null ){
String search = data.getStringExtra( ActAbout.EXTRA_SEARCH );
@ -507,8 +495,21 @@ public class ActMain extends AppCompatActivity
}
}
}
if( requestCode == REQUEST_CODE_APP_SETTING ){
if( requestCode == REQUEST_CODE_ACCOUNT_SETTING ){
updateColumnStrip();
for( Column column : app_state.column_list ){
column.fireShowColumnHeader();
}
if( resultCode == RESULT_OK && data != null ){
startAccessTokenUpdate( data );
}else if( resultCode == ActAccountSetting.RESULT_INPUT_ACCESS_TOKEN && data != null){
long db_id = data.getLongExtra( ActAccountSetting.EXTRA_DB_ID,-1L );
checkAccessToken2( db_id );
}
}else if( requestCode == REQUEST_CODE_APP_SETTING ){
showFooterColor();
if( resultCode == RESULT_APP_DATA_IMPORT ){
@ -1033,7 +1034,12 @@ public class ActMain extends AppCompatActivity
public void performAccountAdd(){
LoginForm.showLoginForm( this, null, new LoginForm.LoginFormCallback() {
@Override
public void startLogin( final Dialog dialog, final String instance, final boolean bPseudoAccount ){
public void startLogin(
final Dialog dialog
, final String instance
, final boolean bPseudoAccount
, final boolean bInputAccessToken
){
final ProgressDialog progress = new ProgressDialog( ActMain.this );
@ -1061,7 +1067,6 @@ public class ActMain extends AppCompatActivity
return api_client.checkInstance();
}else{
return api_client.authorize1( Pref.pref( ActMain.this ).getString( Pref.KEY_CLIENT_NAME, "" ) );
}
}
@ -1088,11 +1093,21 @@ public class ActMain extends AppCompatActivity
// エラーはブラウザ用URLかもしれない
if( sv.startsWith( "https" ) ){
// OAuth認証が必要
Intent data = new Intent();
data.setData( Uri.parse( sv ) );
startAccessTokenUpdate( data );
dialog.dismiss();
if( bInputAccessToken ){
// アクセストークンの手動入力
DlgAccessToken.show( ActMain.this, new DlgAccessToken.Callback() {
@Override public void startCheck( Dialog dialog_token, String access_token ){
checkAccessToken( dialog,dialog_token,instance,access_token ,null);
}
} );
}else{
// OAuth認証が必要
Intent data = new Intent();
data.setData( Uri.parse( sv ) );
startAccessTokenUpdate( data );
dialog.dismiss();
}
return;
}
@ -1367,79 +1382,7 @@ public class ActMain extends AppCompatActivity
// at android.app.Dialog.dismiss(Dialog.java:529)
}
//noinspection StatementWithEmptyBody
if( result == null ){
// cancelled.
}else if( result.error != null ){
Utils.showToast( ActMain.this, true, result.error );
}else if( ta == null ){
// 自分のユーザネームを取れなかった
// 普通はエラーメッセージが設定されてるはずだが
Utils.showToast( ActMain.this, true, "missing TootAccount" );
}else if( this.sa != null ){
// アクセストークン更新時
// インスタンスは同じだと思うがユーザ名が異なる可能性がある
if( ! sa.username.equals( ta.username ) ){
Utils.showToast( ActMain.this, true, R.string.user_name_not_match );
}else{
Utils.showToast( ActMain.this, false, R.string.access_token_updated_for, sa.acct );
// DBの情報を更新する
sa.updateTokenInfo( result.token_info );
// 各カラムの持つアカウント情報をリロードする
reloadAccountSetting();
// 自動でリロードする
for( Column c : app_state.column_list ){
if( c.access_info.acct.equals( sa.acct ) ){
c.startLoading();
}
}
// 通知の更新が必要かもしれない
AlarmService.startCheck( ActMain.this );
}
}else{
// アカウント追加時
String user = ta.username + "@" + host;
long row_id = SavedAccount.insert( host, user, result.object, result.token_info );
SavedAccount account = SavedAccount.loadAccount( ActMain.this, log, row_id );
if( account != null ){
boolean bModified = false;
if( account.locked ){
bModified = true;
account.visibility = TootStatus.VISIBILITY_PRIVATE;
}
if( ta != null && ta.source != null ){
if( ta.source.privacy != null ){
bModified = true;
account.visibility = ta.source.privacy;
}
// FIXME ta.source.sensitive パラメータを読んで添付画像をデフォルトでNSFWにするを実現する
}
if( bModified ){
account.saveSetting();
}
Utils.showToast( ActMain.this, false, R.string.account_confirmed );
// 通知の更新が必要かもしれない
AlarmService.startCheck( ActMain.this );
// 適当にカラムを追加する
long count = SavedAccount.getCount();
if( count > 1 ){
addColumn( getDefaultInsertPosition(), account, Column.TYPE_HOME );
}else{
addColumn( getDefaultInsertPosition(), account, Column.TYPE_HOME );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_NOTIFICATIONS );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_LOCAL );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_FEDERATE );
}
}
}
afterAccountVerify( result,ta,sa,host );
}
};
progress.setIndeterminate( true );
@ -1454,6 +1397,180 @@ public class ActMain extends AppCompatActivity
task.executeOnExecutor( App1.task_executor );
}
boolean afterAccountVerify( @Nullable TootApiResult result, @Nullable TootAccount ta, @Nullable SavedAccount sa, @Nullable String host){
//noinspection StatementWithEmptyBody
if( result == null ){
// cancelled.
}else if( result.error != null ){
Utils.showToast( ActMain.this, true, result.error );
}else if( ta == null ){
// 自分のユーザネームを取れなかった
// 普通はエラーメッセージが設定されてるはずだが
Utils.showToast( ActMain.this, true, "missing TootAccount" );
}else if( sa != null ){
// アクセストークン更新時
// インスタンスは同じだと思うがユーザ名が異なる可能性がある
if( ! sa.username.equals( ta.username ) ){
Utils.showToast( ActMain.this, true, R.string.user_name_not_match );
}else{
Utils.showToast( ActMain.this, false, R.string.access_token_updated_for, sa.acct );
// DBの情報を更新する
sa.updateTokenInfo( result.token_info );
// 各カラムの持つアカウント情報をリロードする
reloadAccountSetting();
// 自動でリロードする
for( Column c : app_state.column_list ){
if( c.access_info.acct.equals( sa.acct ) ){
c.startLoading();
}
}
// 通知の更新が必要かもしれない
AlarmService.startCheck( ActMain.this );
return true;
}
}else{
// アカウント追加時
String user = ta.username + "@" + host;
long row_id = SavedAccount.insert( host, user, result.object, result.token_info );
SavedAccount account = SavedAccount.loadAccount( ActMain.this, log, row_id );
if( account != null ){
boolean bModified = false;
if( account.locked ){
bModified = true;
account.visibility = TootStatus.VISIBILITY_PRIVATE;
}
if( ta.source != null ){
if( ta.source.privacy != null ){
bModified = true;
account.visibility = ta.source.privacy;
}
// FIXME ta.source.sensitive パラメータを読んで添付画像をデフォルトでNSFWにするを実現する
}
if( bModified ){
account.saveSetting();
}
Utils.showToast( ActMain.this, false, R.string.account_confirmed );
// 通知の更新が必要かもしれない
AlarmService.startCheck( ActMain.this );
// 適当にカラムを追加する
long count = SavedAccount.getCount();
if( count > 1 ){
addColumn( getDefaultInsertPosition(), account, Column.TYPE_HOME );
}else{
addColumn( getDefaultInsertPosition(), account, Column.TYPE_HOME );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_NOTIFICATIONS );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_LOCAL );
addColumn( getDefaultInsertPosition(), account, Column.TYPE_FEDERATE );
}
return true;
}
}
return false;
}
// アクセストークンを手動で入力した場合
void checkAccessToken(
@Nullable final Dialog dialog_host
, @Nullable final Dialog dialog_token
, @NonNull final String host
, @NonNull final String access_token
, @Nullable final SavedAccount sa
){
final ProgressDialog progress = new ProgressDialog( ActMain.this );
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
TootAccount ta;
@Override
protected TootApiResult doInBackground( Void... params ){
TootApiClient client = new TootApiClient( ActMain.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.setInstance( host );
TootApiResult result = client.checkAccessToken( access_token );
if( result != null && result.object != null ){
// taは使い捨てなので生成に使うLinkClickContextはダミーで問題ない
LinkClickContext lcc = new LinkClickContext() {
@Override public AcctColor findAcctColor( String url ){
return null;
}
};
this.ta = TootAccount.parse( ActMain.this, log, lcc, result.object );
}
return result;
}
@Override
protected void onPostExecute( TootApiResult result ){
try{
progress.dismiss();
}catch( Throwable ex ){
ex.printStackTrace();
// java.lang.IllegalArgumentException:
// at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:451)
// at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:377)
// at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:122)
// at android.app.Dialog.dismissDialog(Dialog.java:546)
// at android.app.Dialog.dismiss(Dialog.java:529)
}
if( afterAccountVerify( result,ta,sa,host ) ){
if(dialog_host!=null ) dialog_host.dismiss();
if(dialog_token!=null ) dialog_token.dismiss();
}
}
};
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 );
}
// アクセストークンの手動入力(更新)
void checkAccessToken2(long db_id){
final SavedAccount sa = SavedAccount.loadAccount( this,log,db_id );
if( sa == null ) return;
DlgAccessToken.show( this, new DlgAccessToken.Callback() {
@Override public void startCheck( Dialog dialog_token, String access_token ){
checkAccessToken( null, dialog_token, sa.host, access_token ,sa);
}
});
}
void reloadAccountSetting(){
ArrayList< SavedAccount > done_list = new ArrayList<>();
for( Column column : app_state.column_list ){
@ -2662,8 +2779,8 @@ public class ActMain extends AppCompatActivity
Request.Builder request_builder = new Request.Builder()
.post( RequestBody.create( TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
, "id=" + Long.toString( notification.id )
)
);
)
);
return client.request(
"/api/v1/notifications/dismiss"

View File

@ -26,6 +26,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.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
@ -61,6 +62,7 @@ import jp.juggler.subwaytooter.api.entity.TootAttachment;
import jp.juggler.subwaytooter.api.entity.TootMention;
import jp.juggler.subwaytooter.api.entity.TootResults;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.dialog.AccountPicker;
import jp.juggler.subwaytooter.dialog.DlgDraftPicker;
import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.table.PostDraft;
@ -694,9 +696,28 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
void setAccount( SavedAccount a ){
this.account = a;
btnAccount.setText(
( a == null ? getString( R.string.not_selected ) : a.getFullAcct( a ) )
);
SpannableStringBuilder sb = new SpannableStringBuilder();
if( a == null ){
btnAccount.setText( getString( R.string.not_selected ) );
btnAccount.setTextColor( Styler.getAttributeColor( this, android.R.attr.textColorPrimary ) );
btnAccount.setBackgroundResource( R.drawable.btn_bg_transparent );
}else{
String acct = a.getFullAcct( a );
AcctColor ac = AcctColor.load( acct );
String nickname = AcctColor.hasNickname( ac ) ? ac.nickname : acct;
btnAccount.setText( nickname );
if( AcctColor.hasColorBackground( ac ) ){
btnAccount.setBackgroundColor( ac.color_bg );
}else{
btnAccount.setBackgroundResource( R.drawable.btn_bg_transparent );
}
if( AcctColor.hasColorForeground( ac ) ){
btnAccount.setTextColor( ac.color_fg );
}else{
btnAccount.setTextColor( Styler.getAttributeColor( this, android.R.attr.textColorPrimary ) );
}
}
}
private void performAccountChooser(){
@ -707,42 +728,59 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
return;
}
final ArrayList< SavedAccount > tmp_account_list = new ArrayList<>();
tmp_account_list.addAll( account_list );
String[] caption_list = new String[ tmp_account_list.size() ];
for( int i = 0, ie = tmp_account_list.size() ; i < ie ; ++ i ){
caption_list[ i ] = tmp_account_list.get( i ).acct;
}
new AlertDialog.Builder( this )
.setTitle( R.string.choose_account )
.setItems( caption_list, new DialogInterface.OnClickListener() {
@Override
public void onClick( DialogInterface dialog, int which ){
if( which < 0 || which >= tmp_account_list.size() ){
// 範囲外
return;
AccountPicker.pick( this, false, false, getString( R.string.choose_account ), new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( @NonNull SavedAccount ai ){
if( ! ai.host.equals( account.host ) ){
// 別タンスへの移動
if( in_reply_to_id != - 1L ){
// 別タンスのアカウントならin_reply_toの変換が必要
startReplyConversion( ai );
}
SavedAccount a = tmp_account_list.get( which );
if( ! a.host.equals( account.host ) ){
// 別タンスへの移動
if( in_reply_to_id != - 1L ){
// 別タンスのアカウントならin_reply_toの変換が必要
startReplyConversion( a );
}
}
// リプライがないか同タンスへの移動
setAccountWithVisibilityConversion( a );
}
} )
.setNegativeButton( R.string.cancel, null )
.show();
// リプライがないか同タンスへの移動
setAccountWithVisibilityConversion( ai );
}
} );
// final ArrayList< SavedAccount > tmp_account_list = new ArrayList<>();
// tmp_account_list.addAll( account_list );
//
// String[] caption_list = new String[ tmp_account_list.size() ];
// for( int i = 0, ie = tmp_account_list.size() ; i < ie ; ++ i ){
// caption_list[ i ] = tmp_account_list.get( i ).acct;
// }
//
// new AlertDialog.Builder( this )
// .setTitle( R.string.choose_account )
// .setItems( caption_list, new DialogInterface.OnClickListener() {
// @Override
// public void onClick( DialogInterface dialog, int which ){
//
// if( which < 0 || which >= tmp_account_list.size() ){
// // 範囲外
// return;
// }
//
// SavedAccount ai = tmp_account_list.get( which );
//
// if( ! ai.host.equals( account.host ) ){
// // 別タンスへの移動
// if( in_reply_to_id != - 1L ){
// // 別タンスのアカウントならin_reply_toの変換が必要
// startReplyConversion( ai );
//
// }
// }
//
// // リプライがないか同タンスへの移動
// setAccountWithVisibilityConversion( ai );
// }
// } )
// .setNegativeButton( R.string.cancel, null )
// .show();
}
void setAccountWithVisibilityConversion( @NonNull SavedAccount a ){

View File

@ -315,7 +315,7 @@ public class TootApiClient {
return new TootApiResult( browser_url );
}
public @Nullable TootApiResult authorize2( String code ){
public @Nullable TootApiResult authorize2( String code ){
JSONObject client_info = ClientInfo.load( instance );
if( client_info == null ){
@ -424,4 +424,66 @@ public class TootApiClient {
}
}
public @Nullable TootApiResult checkAccessToken( String access_token ){
JSONObject client_info = ClientInfo.load( instance );
if( client_info == null ){
return new TootApiResult( "missing client id" );
}
JSONObject token_info;
Response response;
try{
// 指定されたアクセストークンを使って token_info を捏造する
token_info = new JSONObject( );
token_info.put("access_token",access_token);
// 認証されたアカウントのユーザ名を取得する
String path = "/api/v1/accounts/verify_credentials";
callback.publishApiProgress( context.getString( R.string.request_api, path ) );
Request request = new Request.Builder()
.url( "https://" + instance + path )
.header( "Authorization", "Bearer " + Utils.optStringX( token_info, "access_token" ) )
.build();
Call call = ok_http_client.newCall( request );
response = call.execute();
}catch( Throwable ex ){
ex.printStackTrace();
return new TootApiResult( instance + ": " +Utils.formatError( ex, context.getResources(), R.string.network_error ) );
}
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( instance + ": " + context.getString( R.string.network_error_arg, response ) );
}
try{
//noinspection ConstantConditions
String json = response.body().string();
if( TextUtils.isEmpty( json ) || json.startsWith( "<" ) ){
return new TootApiResult( context.getString( R.string.response_not_json ) + "\n" + json );
}else if( json.startsWith( "[" ) ){
JSONArray array = new JSONArray( json );
return new TootApiResult( log, response, token_info, json, array );
}else{
JSONObject object = new JSONObject( json );
String error = Utils.optStringX( object, "error" );
if( ! TextUtils.isEmpty( error ) ){
return new TootApiResult( context.getString( R.string.api_error, error ) );
}
return new TootApiResult( response, token_info, json, object );
}
}catch( Throwable ex ){
ex.printStackTrace();
return new TootApiResult( Utils.formatError( ex, "API data error" ) );
}
}
}

View File

@ -5,6 +5,7 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
@ -32,7 +33,7 @@ public class AccountPicker {
public static void pick(
@NonNull ActMain activity
@NonNull AppCompatActivity activity
, boolean bAllowPseudo
, boolean bAuto
, String message
@ -44,7 +45,7 @@ public class AccountPicker {
}
public static void pick(
@NonNull ActMain activity
@NonNull AppCompatActivity activity
, boolean bAllowPseudo
, boolean bAuto
, String message
@ -56,7 +57,7 @@ public class AccountPicker {
}
public static void pick(
@NonNull ActMain activity
@NonNull AppCompatActivity activity
, boolean bAllowPseudo
, boolean bAuto
, String message
@ -67,7 +68,7 @@ public class AccountPicker {
}
public static void pick(
@NonNull ActMain activity
@NonNull AppCompatActivity activity
, boolean bAllowPseudo
, boolean bAuto
, String message
@ -135,11 +136,12 @@ public class AccountPicker {
} );
float density = activity.getResources().getDisplayMetrics().density;
LinearLayout llAccounts = (LinearLayout) viewRoot.findViewById( R.id.llAccounts );
int pad_se = (int) ( 0.5f + 12f * activity.density );
int pad_tb = (int) ( 0.5f + 6f * activity.density );
int pad_se = (int) ( 0.5f + 12f * density );
int pad_tb = (int) ( 0.5f + 6f * density );
for( SavedAccount a : account_list ){
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
@ -161,7 +163,7 @@ public class AccountPicker {
b.setGravity( Gravity.START | Gravity.CENTER_VERTICAL );
b.setAllCaps( false );
b.setLayoutParams( lp );
b.setMinHeight( (int) ( 0.5f + 32f * activity.density ) );
b.setMinHeight( (int) ( 0.5f + 32f * density ) );
b.setText( AcctColor.hasNickname( ac ) ? ac.nickname : a.acct );
final SavedAccount _a = a;

View File

@ -0,0 +1,67 @@
package jp.juggler.subwaytooter.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.util.Utils;
public class DlgAccessToken {
public interface Callback {
void startCheck( Dialog dialog, String access_token );
}
public static void show( @NonNull final Activity activity ,@NonNull final Callback callback ){
@SuppressLint("InflateParams") final View view = activity.getLayoutInflater().inflate( R.layout.dlg_access_token, null, false );
final EditText etToken = (EditText) view.findViewById( R.id.etToken );
final View btnOk = view.findViewById( R.id.btnOk );
etToken.setOnEditorActionListener( new TextView.OnEditorActionListener() {
@Override public boolean onEditorAction( TextView v, int actionId, KeyEvent event ){
if( actionId == EditorInfo.IME_ACTION_DONE ){
btnOk.performClick();
return true;
}
return false;
}
} );
final Dialog dialog = new Dialog( activity );
dialog.setContentView( view );
btnOk.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ){
final String token = etToken.getText().toString().trim();
if( TextUtils.isEmpty( token ) ){
Utils.showToast( activity, true, R.string.token_not_specified );
return;
}
callback.startCheck( dialog, token );
}
} );
view.findViewById( R.id.btnCancel ).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick( View v ){
dialog.cancel();
}
} );
//noinspection ConstantConditions
dialog.getWindow().setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT );
dialog.show();
}
}

View File

@ -13,6 +13,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Filter;
import android.widget.TextView;
@ -27,7 +28,10 @@ import jp.juggler.subwaytooter.util.Utils;
public class LoginForm {
public interface LoginFormCallback {
void startLogin( Dialog dialog, String instance, boolean bPseudoAccount );
void startLogin( Dialog dialog, String instance
, boolean bPseudoAccount
, boolean bInputAccessToken
);
}
private static class StringArray extends ArrayList< String > {
@ -39,6 +43,13 @@ public class LoginForm {
final AutoCompleteTextView etInstance = (AutoCompleteTextView) view.findViewById( R.id.etInstance );
final View btnOk = view.findViewById( R.id.btnOk );
final CheckBox cbPseudoAccount = (CheckBox) view.findViewById( R.id.cbPseudoAccount );
final CheckBox cbInputAccessToken = (CheckBox) view.findViewById( R.id.cbInputAccessToken );
cbPseudoAccount.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override public void onCheckedChanged( CompoundButton buttonView, boolean isChecked ){
cbInputAccessToken.setEnabled( ! cbPseudoAccount.isChecked() );
}
} );
if( ! TextUtils.isEmpty( instance ) ){
etInstance.setText( instance );
@ -70,7 +81,10 @@ public class LoginForm {
Utils.showToast( activity, true, R.string.instance_not_need_slash );
return;
}
callback.startLogin( dialog, instance, cbPseudoAccount.isChecked() );
callback.startLogin( dialog, instance
, cbPseudoAccount.isChecked()
, cbInputAccessToken.isChecked()
);
}
} );
view.findViewById( R.id.btnCancel ).setOnClickListener( new View.OnClickListener() {

View File

@ -112,7 +112,17 @@
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnInputAccessToken"
style="@style/setting_horizontal_stretch"
android:ellipsize="start"
android:text="@string/input_access_token"
android:textAllCaps="false"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:labelFor="@+id/etToken"
android:text="@string/access_token"
/>
<EditText
android:id="@+id/etToken"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:imeOptions="actionDone"
android:inputType="text"
/>
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/btnCancel"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"
/>
<Button
android:id="@+id/btnOk"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/ok"
/>
</LinearLayout>
</LinearLayout>

View File

@ -12,8 +12,8 @@
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/instance"
android:labelFor="@+id/etInstance"
android:text="@string/instance"
/>
<AutoCompleteTextView
@ -22,17 +22,27 @@
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:inputType="textUri"
android:hint="@string/instance_hint"
android:imeOptions="actionDone"
android:inputType="textUri"
/>
<CheckBox
android:id="@+id/cbPseudoAccount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:text="@string/pseudo_account"
android:id="@+id/cbPseudoAccount"
/>
<CheckBox
android:id="@+id/cbInputAccessToken"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp"
android:text="@string/input_access_token"
/>
<LinearLayout

View File

@ -397,10 +397,13 @@
<string name="quote_url">Quote URL</string>
<string name="actions_for_notification">Actions for notification</string>
<string name="delete_this_notification">Delete this notification</string>
<string name="mute_this_conversation">Mute more notification for this conversation</string>
<string name="unmute_this_conversation">Unmute more notification for this conversation</string>
<string name="mute_this_conversation">Mute more notifications for this conversation</string>
<string name="unmute_this_conversation">Unmute more notifications for this conversation</string>
<string name="input_access_token">Specify access token (for advanced users)</string>
<string name="access_token">Access token</string>
<string name="token_not_specified">Please input access token.</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_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->

View File

@ -684,7 +684,10 @@
<string name="quote_url">URLを引用…</string>
<string name="actions_for_notification">通知へのアクション</string>
<string name="delete_this_notification">この通知を削除</string>
<string name="mute_this_conversation">この会話の通知をミュート</string>
<string name="unmute_this_conversation">この会話の通知をミュート解除</string>
<string name="mute_this_conversation">この会話の次回以降の通知をミュート</string>
<string name="unmute_this_conversation">この会話の次回以降の通知をミュート解除</string>
<string name="access_token">アクセストークン</string>
<string name="input_access_token">アクセストークンを指定する(上級者向け)</string>
<string name="token_not_specified">アクセストークンを入力してください</string>
</resources>

View File

@ -392,7 +392,10 @@
<string name="quote_url">Quote URL</string>
<string name="actions_for_notification">Actions for notification</string>
<string name="delete_this_notification">Delete this notification</string>
<string name="mute_this_conversation">Mute this conversation</string>
<string name="unmute_this_conversation">Unmute this conversation</string>
<string name="mute_this_conversation">Mute more notifications for this conversation</string>
<string name="unmute_this_conversation">Unmute more notifications for this conversation</string>
<string name="input_access_token">Specify access token (for advanced users)</string>
<string name="access_token">Access token</string>
<string name="token_not_specified">Please input access token.</string>
</resources>