v1.1.9
- リモートユーザのプロフィールに警告を表示 - トゥートの公開範囲をTL中に表示する - アカウント設定にプロフィール編集を追加
|
@ -9,8 +9,8 @@ android {
|
||||||
applicationId "jp.juggler.subwaytooter"
|
applicationId "jp.juggler.subwaytooter"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 118
|
versionCode 119
|
||||||
versionName "1.1.8"
|
versionName "1.1.9"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,58 @@
|
||||||
package jp.juggler.subwaytooter;
|
package jp.juggler.subwaytooter;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
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.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Base64OutputStream;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient;
|
import jp.juggler.subwaytooter.api.TootApiClient;
|
||||||
import jp.juggler.subwaytooter.api.TootApiResult;
|
import jp.juggler.subwaytooter.api.TootApiResult;
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootAccount;
|
||||||
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
import jp.juggler.subwaytooter.api.entity.TootStatus;
|
||||||
import jp.juggler.subwaytooter.dialog.DlgAccessToken;
|
import jp.juggler.subwaytooter.dialog.ActionsDialog;
|
||||||
import jp.juggler.subwaytooter.table.AcctColor;
|
import jp.juggler.subwaytooter.table.AcctColor;
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||||
|
import jp.juggler.subwaytooter.util.Emojione;
|
||||||
import jp.juggler.subwaytooter.util.LogCategory;
|
import jp.juggler.subwaytooter.util.LogCategory;
|
||||||
import jp.juggler.subwaytooter.util.Utils;
|
import jp.juggler.subwaytooter.util.Utils;
|
||||||
|
import jp.juggler.subwaytooter.view.MyNetworkImageView;
|
||||||
import okhttp3.Call;
|
import okhttp3.Call;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
|
@ -50,16 +74,22 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
SavedAccount account;
|
SavedAccount account;
|
||||||
|
SharedPreferences pref;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate( @Nullable Bundle savedInstanceState ){
|
protected void onCreate( @Nullable Bundle savedInstanceState ){
|
||||||
super.onCreate( savedInstanceState );
|
super.onCreate( savedInstanceState );
|
||||||
|
|
||||||
App1.setActivityTheme( this, false );
|
App1.setActivityTheme( this, false );
|
||||||
|
this.pref = App1.pref;
|
||||||
|
|
||||||
initUI();
|
initUI();
|
||||||
account = SavedAccount.loadAccount( this, log, getIntent().getLongExtra( KEY_ACCOUNT_DB_ID, - 1L ) );
|
account = SavedAccount.loadAccount( this, log, getIntent().getLongExtra( KEY_ACCOUNT_DB_ID, - 1L ) );
|
||||||
if( account == null ) finish();
|
if( account == null ) finish();
|
||||||
loadUIFromData( account );
|
loadUIFromData( account );
|
||||||
|
|
||||||
|
initializeProfile();
|
||||||
|
|
||||||
btnOpenBrowser.setText( getString( R.string.open_instance_website, account.host ) );
|
btnOpenBrowser.setText( getString( R.string.open_instance_website, account.host ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,26 +100,89 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
|
|
||||||
static final int REQUEST_CODE_ACCT_CUSTOMIZE = 1;
|
static final int REQUEST_CODE_ACCT_CUSTOMIZE = 1;
|
||||||
static final int REQUEST_CODE_NOTIFICATION_SOUND = 2;
|
static final int REQUEST_CODE_NOTIFICATION_SOUND = 2;
|
||||||
|
private static final int REQUEST_CODE_AVATAR_ATTACHMENT = 3;
|
||||||
|
private static final int REQUEST_CODE_HEADER_ATTACHMENT = 4;
|
||||||
|
private static final int REQUEST_CODE_AVATAR_CAMERA = 5;
|
||||||
|
private static final int REQUEST_CODE_HEADER_CAMERA = 6;
|
||||||
|
|
||||||
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){
|
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){
|
||||||
if( requestCode == REQUEST_CODE_ACCT_CUSTOMIZE && resultCode == RESULT_OK ){
|
switch( requestCode ){
|
||||||
showAcctColor();
|
default:
|
||||||
}else if( resultCode == RESULT_OK && requestCode == REQUEST_CODE_NOTIFICATION_SOUND ){
|
super.onActivityResult( requestCode, resultCode, data );
|
||||||
// RINGTONE_PICKERからの選択されたデータを取得する
|
break;
|
||||||
Uri uri = (Uri) data.getExtras().get( RingtoneManager.EXTRA_RINGTONE_PICKED_URI );
|
case REQUEST_CODE_ACCT_CUSTOMIZE:{
|
||||||
if( uri != null ){
|
if( resultCode == RESULT_OK ){
|
||||||
notification_sound_uri = uri.toString();
|
showAcctColor();
|
||||||
saveUIToData();
|
|
||||||
// Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), uri);
|
|
||||||
// TextView ringView = (TextView) findViewById(R.id.ringtone);
|
|
||||||
// ringView.setText(ringtone.getTitle(getApplicationContext()));
|
|
||||||
// ringtone.setStreamType(AudioManager.STREAM_ALARM);
|
|
||||||
// ringtone.play();
|
|
||||||
// SystemClock.sleep(1000);
|
|
||||||
// ringtone.stop();
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_NOTIFICATION_SOUND:{
|
||||||
|
if( resultCode == RESULT_OK ){
|
||||||
|
// RINGTONE_PICKERからの選択されたデータを取得する
|
||||||
|
Uri uri = (Uri) data.getExtras().get( RingtoneManager.EXTRA_RINGTONE_PICKED_URI );
|
||||||
|
if( uri != null ){
|
||||||
|
notification_sound_uri = uri.toString();
|
||||||
|
saveUIToData();
|
||||||
|
// Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), uri);
|
||||||
|
// TextView ringView = (TextView) findViewById(R.id.ringtone);
|
||||||
|
// ringView.setText(ringtone.getTitle(getApplicationContext()));
|
||||||
|
// ringtone.setStreamType(AudioManager.STREAM_ALARM);
|
||||||
|
// ringtone.play();
|
||||||
|
// SystemClock.sleep(1000);
|
||||||
|
// ringtone.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_AVATAR_ATTACHMENT:
|
||||||
|
case REQUEST_CODE_HEADER_ATTACHMENT:{
|
||||||
|
|
||||||
|
if( resultCode == RESULT_OK && data != null ){
|
||||||
|
Uri uri = data.getData();
|
||||||
|
if( uri != null ){
|
||||||
|
// 単一選択
|
||||||
|
String type = data.getType();
|
||||||
|
if( TextUtils.isEmpty( type ) ){
|
||||||
|
type = getContentResolver().getType( uri );
|
||||||
|
}
|
||||||
|
addAttachment( requestCode, uri, type );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ClipData cd = data.getClipData();
|
||||||
|
if( cd != null ){
|
||||||
|
int count = cd.getItemCount();
|
||||||
|
if( count > 0 ){
|
||||||
|
ClipData.Item item = cd.getItemAt( 0 );
|
||||||
|
uri = item.getUri();
|
||||||
|
String type = getContentResolver().getType( uri );
|
||||||
|
addAttachment( requestCode, uri, type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_AVATAR_CAMERA:
|
||||||
|
case REQUEST_CODE_HEADER_CAMERA:{
|
||||||
|
|
||||||
|
if( resultCode != RESULT_OK ){
|
||||||
|
// 失敗したら DBからデータを削除
|
||||||
|
if( uriCameraImage != null ){
|
||||||
|
getContentResolver().delete( uriCameraImage, null, null );
|
||||||
|
uriCameraImage = null;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
// 画像のURL
|
||||||
|
Uri uri = ( data == null ? null : data.getData() );
|
||||||
|
if( uri == null ) uri = uriCameraImage;
|
||||||
|
|
||||||
|
if( uri != null ){
|
||||||
|
String type = getContentResolver().getType( uri );
|
||||||
|
addAttachment( requestCode, uri, type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult( requestCode, resultCode, data );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextView tvInstance;
|
TextView tvInstance;
|
||||||
|
@ -120,6 +213,15 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
|
|
||||||
String notification_sound_uri;
|
String notification_sound_uri;
|
||||||
|
|
||||||
|
MyNetworkImageView ivProfileHeader;
|
||||||
|
MyNetworkImageView ivProfileAvatar;
|
||||||
|
View btnProfileAvatar;
|
||||||
|
View btnProfileHeader;
|
||||||
|
EditText etDisplayName;
|
||||||
|
View btnDisplayName;
|
||||||
|
EditText etNote;
|
||||||
|
View btnNote;
|
||||||
|
|
||||||
private void initUI(){
|
private void initUI(){
|
||||||
setContentView( R.layout.act_account_setting );
|
setContentView( R.layout.act_account_setting );
|
||||||
|
|
||||||
|
@ -147,12 +249,25 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
tvUserCustom = (TextView) findViewById( R.id.tvUserCustom );
|
tvUserCustom = (TextView) findViewById( R.id.tvUserCustom );
|
||||||
btnUserCustom = findViewById( R.id.btnUserCustom );
|
btnUserCustom = findViewById( R.id.btnUserCustom );
|
||||||
|
|
||||||
|
ivProfileHeader = (MyNetworkImageView) findViewById( R.id.ivProfileHeader );
|
||||||
|
ivProfileAvatar = (MyNetworkImageView) findViewById( R.id.ivProfileAvatar );
|
||||||
|
btnProfileAvatar = findViewById( R.id.btnProfileAvatar );
|
||||||
|
btnProfileHeader = findViewById( R.id.btnProfileHeader );
|
||||||
|
etDisplayName = (EditText) findViewById( R.id.etDisplayName );
|
||||||
|
btnDisplayName = findViewById( R.id.btnDisplayName );
|
||||||
|
etNote = (EditText) findViewById( R.id.etNote );
|
||||||
|
btnNote = findViewById( R.id.btnNote );
|
||||||
|
|
||||||
btnOpenBrowser.setOnClickListener( this );
|
btnOpenBrowser.setOnClickListener( this );
|
||||||
btnAccessToken.setOnClickListener( this );
|
btnAccessToken.setOnClickListener( this );
|
||||||
btnInputAccessToken.setOnClickListener( this );
|
btnInputAccessToken.setOnClickListener( this );
|
||||||
btnAccountRemove.setOnClickListener( this );
|
btnAccountRemove.setOnClickListener( this );
|
||||||
btnVisibility.setOnClickListener( this );
|
btnVisibility.setOnClickListener( this );
|
||||||
btnUserCustom.setOnClickListener( this );
|
btnUserCustom.setOnClickListener( this );
|
||||||
|
btnProfileAvatar.setOnClickListener( this );
|
||||||
|
btnProfileHeader.setOnClickListener( this );
|
||||||
|
btnDisplayName.setOnClickListener( this );
|
||||||
|
btnNote.setOnClickListener( this );
|
||||||
|
|
||||||
swNSFWOpen.setOnCheckedChangeListener( this );
|
swNSFWOpen.setOnCheckedChangeListener( this );
|
||||||
cbNotificationMention.setOnCheckedChangeListener( this );
|
cbNotificationMention.setOnCheckedChangeListener( this );
|
||||||
|
@ -290,6 +405,23 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
notification_sound_uri = "";
|
notification_sound_uri = "";
|
||||||
saveUIToData();
|
saveUIToData();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case R.id.btnProfileAvatar:
|
||||||
|
pickAvatarImage();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.id.btnProfileHeader:
|
||||||
|
pickHeaderImage();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.id.btnDisplayName:
|
||||||
|
sendDisplayName();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R.id.btnNote:
|
||||||
|
sendNote();
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,5 +650,437 @@ public class ActAccountSetting extends AppCompatActivity
|
||||||
startActivityForResult( chooser, REQUEST_CODE_NOTIFICATION_SOUND );
|
startActivityForResult( chooser, REQUEST_CODE_NOTIFICATION_SOUND );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void initializeProfile(){
|
||||||
|
// 初期状態
|
||||||
|
ivProfileAvatar.setErrorImageResId( Styler.getAttributeResourceId( this, R.attr.ic_question ) );
|
||||||
|
ivProfileAvatar.setDefaultImageResId( Styler.getAttributeResourceId( this, R.attr.ic_question ) );
|
||||||
|
etDisplayName.setText( "(loading...)" );
|
||||||
|
etNote.setText( "(loading...)" );
|
||||||
|
// 初期状態では編集不可能
|
||||||
|
btnProfileAvatar.setEnabled( false );
|
||||||
|
btnProfileHeader.setEnabled( false );
|
||||||
|
etDisplayName.setEnabled( false );
|
||||||
|
btnDisplayName.setEnabled( false );
|
||||||
|
etNote.setEnabled( false );
|
||||||
|
btnNote.setEnabled( false );
|
||||||
|
// 疑似アカウントなら編集不可のまま
|
||||||
|
if( account.isPseudo() ) return;
|
||||||
|
|
||||||
|
loadProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadProfile(){
|
||||||
|
// サーバから情報をロードする
|
||||||
|
|
||||||
|
final ProgressDialog progress = new ProgressDialog( this );
|
||||||
|
|
||||||
|
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
||||||
|
|
||||||
|
TootAccount data;
|
||||||
|
|
||||||
|
@Override protected TootApiResult doInBackground( Void... params ){
|
||||||
|
TootApiClient client = new TootApiClient( ActAccountSetting.this, new TootApiClient.Callback() {
|
||||||
|
@Override public boolean isApiCancelled(){
|
||||||
|
return isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void publishApiProgress( final String s ){
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
client.setAccount( account );
|
||||||
|
|
||||||
|
TootApiResult result = client.request( "/api/v1/accounts/verify_credentials" );
|
||||||
|
if( result != null && result.object != null ){
|
||||||
|
data = TootAccount.parse( ActAccountSetting.this, account, result.object );
|
||||||
|
if( data == null ) return new TootApiResult( "TootAccount parse failed." );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled( TootApiResult result ){
|
||||||
|
super.onPostExecute( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute( TootApiResult result ){
|
||||||
|
try{
|
||||||
|
progress.dismiss();
|
||||||
|
}catch( Throwable ignored ){
|
||||||
|
}
|
||||||
|
if( result == null ){
|
||||||
|
// cancelled.
|
||||||
|
}else if( data != null ){
|
||||||
|
showProfile( data );
|
||||||
|
}else{
|
||||||
|
Utils.showToast( ActAccountSetting.this, true, result.error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
task.executeOnExecutor( App1.task_executor );
|
||||||
|
progress.setIndeterminate( true );
|
||||||
|
progress.setOnDismissListener( new DialogInterface.OnDismissListener() {
|
||||||
|
@Override public void onDismiss( DialogInterface dialog ){
|
||||||
|
task.cancel( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
progress.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showProfile( TootAccount src ){
|
||||||
|
ivProfileAvatar.setImageUrl( App1.pref, 16f, src.avatar_static, src.avatar );
|
||||||
|
ivProfileHeader.setImageUrl( App1.pref, 0f, src.header_static, src.header );
|
||||||
|
|
||||||
|
etDisplayName.setText( Emojione.decodeEmoji( this, src.display_name == null ? "" : src.display_name ) );
|
||||||
|
|
||||||
|
String note;
|
||||||
|
if( src.source != null && src.source.note != null ){
|
||||||
|
note = src.source.note;
|
||||||
|
}else{
|
||||||
|
note = src.note;
|
||||||
|
}
|
||||||
|
etNote.setText( Emojione.decodeEmoji( this, note == null ? "" : note ) );
|
||||||
|
|
||||||
|
// 編集可能にする
|
||||||
|
btnProfileAvatar.setEnabled( true );
|
||||||
|
btnProfileHeader.setEnabled( true );
|
||||||
|
etDisplayName.setEnabled( true );
|
||||||
|
btnDisplayName.setEnabled( true );
|
||||||
|
etNote.setEnabled( true );
|
||||||
|
btnNote.setEnabled( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCredential( final String form_data ){
|
||||||
|
final ProgressDialog progress = new ProgressDialog( this );
|
||||||
|
|
||||||
|
final AsyncTask< Void, Void, TootApiResult > task = new AsyncTask< Void, Void, TootApiResult >() {
|
||||||
|
|
||||||
|
TootAccount data;
|
||||||
|
|
||||||
|
@Override protected TootApiResult doInBackground( Void... params ){
|
||||||
|
TootApiClient client = new TootApiClient( ActAccountSetting.this, new TootApiClient.Callback() {
|
||||||
|
@Override public boolean isApiCancelled(){
|
||||||
|
return isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void publishApiProgress( final String s ){
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
client.setAccount( account );
|
||||||
|
|
||||||
|
Request.Builder request_builder = new Request.Builder()
|
||||||
|
.patch( RequestBody.create(
|
||||||
|
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
|
||||||
|
, form_data
|
||||||
|
) );
|
||||||
|
|
||||||
|
TootApiResult result = client.request( "/api/v1/accounts/update_credentials", request_builder );
|
||||||
|
if( result != null && result.object != null ){
|
||||||
|
data = TootAccount.parse( ActAccountSetting.this, account, result.object );
|
||||||
|
if( data == null ) return new TootApiResult( "TootAccount parse failed." );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled( TootApiResult result ){
|
||||||
|
super.onPostExecute( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute( TootApiResult result ){
|
||||||
|
try{
|
||||||
|
progress.dismiss();
|
||||||
|
}catch( Throwable ignored ){
|
||||||
|
}
|
||||||
|
loadProfile();
|
||||||
|
if( result == null ){
|
||||||
|
// cancelled.
|
||||||
|
}else if( data != null ){
|
||||||
|
showProfile( data );
|
||||||
|
}else{
|
||||||
|
Utils.showToast( ActAccountSetting.this, true, result.error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
task.executeOnExecutor( App1.task_executor );
|
||||||
|
progress.setIndeterminate( true );
|
||||||
|
progress.setOnDismissListener( new DialogInterface.OnDismissListener() {
|
||||||
|
@Override public void onDismiss( DialogInterface dialog ){
|
||||||
|
task.cancel( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
progress.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDisplayName(){
|
||||||
|
updateCredential( "display_name=" + Uri.encode( etDisplayName.getText().toString() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendNote(){
|
||||||
|
updateCredential( "note=" + Uri.encode( etNote.getText().toString() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int PERMISSION_REQUEST_AVATAR = 1;
|
||||||
|
private static final int PERMISSION_REQUEST_HEADER = 2;
|
||||||
|
|
||||||
|
private void pickAvatarImage(){
|
||||||
|
openPicker( PERMISSION_REQUEST_AVATAR );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickHeaderImage(){
|
||||||
|
openPicker( PERMISSION_REQUEST_HEADER );
|
||||||
|
}
|
||||||
|
|
||||||
|
void openPicker( final int permission_request_code ){
|
||||||
|
int permissionCheck = ContextCompat.checkSelfPermission( this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE );
|
||||||
|
if( permissionCheck != PackageManager.PERMISSION_GRANTED ){
|
||||||
|
preparePermission( permission_request_code );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsDialog a = new ActionsDialog();
|
||||||
|
a.addAction( getString( R.string.image_pick ), new Runnable() {
|
||||||
|
@Override public void run(){
|
||||||
|
performAttachment( permission_request_code == PERMISSION_REQUEST_AVATAR ? REQUEST_CODE_AVATAR_ATTACHMENT : REQUEST_CODE_HEADER_ATTACHMENT );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
a.addAction( getString( R.string.image_capture ), new Runnable() {
|
||||||
|
@Override public void run(){
|
||||||
|
performCamera( permission_request_code == PERMISSION_REQUEST_AVATAR ? REQUEST_CODE_AVATAR_CAMERA : REQUEST_CODE_HEADER_CAMERA );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
a.show( this, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preparePermission( int request_code ){
|
||||||
|
if( Build.VERSION.SDK_INT >= 23 ){
|
||||||
|
// No explanation needed, we can request the permission.
|
||||||
|
|
||||||
|
ActivityCompat.requestPermissions( this
|
||||||
|
, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }
|
||||||
|
, request_code
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Utils.showToast( this, true, R.string.missing_storage_permission );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onRequestPermissionsResult(
|
||||||
|
int requestCode
|
||||||
|
, @NonNull String permissions[]
|
||||||
|
, @NonNull int[] grantResults
|
||||||
|
){
|
||||||
|
switch( requestCode ){
|
||||||
|
case PERMISSION_REQUEST_AVATAR:
|
||||||
|
case PERMISSION_REQUEST_HEADER:
|
||||||
|
// If request is cancelled, the result arrays are empty.
|
||||||
|
if( grantResults.length > 0 &&
|
||||||
|
grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED
|
||||||
|
){
|
||||||
|
openPicker( requestCode );
|
||||||
|
}else{
|
||||||
|
Utils.showToast( this, true, R.string.missing_storage_permission );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performAttachment( final int request_code ){
|
||||||
|
// SAFのIntentで開く
|
||||||
|
try{
|
||||||
|
Intent intent = new Intent( Intent.ACTION_OPEN_DOCUMENT );
|
||||||
|
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
||||||
|
intent.setType( "*/*" );
|
||||||
|
intent.putExtra( Intent.EXTRA_ALLOW_MULTIPLE, false );
|
||||||
|
intent.putExtra( Intent.EXTRA_MIME_TYPES, new String[]{ "image/*", "video/*" } );
|
||||||
|
startActivityForResult( intent
|
||||||
|
, request_code );
|
||||||
|
}catch( Throwable ex ){
|
||||||
|
log.trace( ex );
|
||||||
|
Utils.showToast( this, ex, "ACTION_OPEN_DOCUMENT failed." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uriCameraImage;
|
||||||
|
|
||||||
|
private void performCamera( final int request_code ){
|
||||||
|
|
||||||
|
try{
|
||||||
|
// カメラで撮影
|
||||||
|
String filename = System.currentTimeMillis() + ".jpg";
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put( MediaStore.Images.Media.TITLE, filename );
|
||||||
|
values.put( MediaStore.Images.Media.MIME_TYPE, "image/jpeg" );
|
||||||
|
uriCameraImage = getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values );
|
||||||
|
|
||||||
|
Intent intent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE );
|
||||||
|
intent.putExtra( MediaStore.EXTRA_OUTPUT, uriCameraImage );
|
||||||
|
|
||||||
|
startActivityForResult( intent, request_code );
|
||||||
|
}catch( Throwable ex ){
|
||||||
|
log.trace( ex );
|
||||||
|
Utils.showToast( this, ex, "opening camera app failed." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InputStreamOpener {
|
||||||
|
InputStream open() throws IOException;
|
||||||
|
|
||||||
|
String getMimeType();
|
||||||
|
|
||||||
|
void deleteTempFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String MIME_TYPE_JPEG = "image/jpeg";
|
||||||
|
static final String MIME_TYPE_PNG = "image/png";
|
||||||
|
|
||||||
|
private InputStreamOpener createOpener( final Uri uri, final String mime_type ){
|
||||||
|
//noinspection LoopStatementThatDoesntLoop
|
||||||
|
for( ; ; ){
|
||||||
|
try{
|
||||||
|
|
||||||
|
// 画像の種別
|
||||||
|
boolean is_jpeg = MIME_TYPE_JPEG.equals( mime_type );
|
||||||
|
boolean is_png = MIME_TYPE_PNG.equals( mime_type );
|
||||||
|
if( ! is_jpeg && ! is_png ){
|
||||||
|
log.d( "createOpener: source is not jpeg or png" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設定からリサイズ指定を読む
|
||||||
|
int resize_to = 1280;
|
||||||
|
|
||||||
|
Bitmap bitmap = Utils.createResizedBitmap( log, this, uri, false, resize_to );
|
||||||
|
if( bitmap != null ){
|
||||||
|
try{
|
||||||
|
File cache_dir = getExternalCacheDir();
|
||||||
|
if( cache_dir == null ){
|
||||||
|
Utils.showToast( this, false, "getExternalCacheDir returns null." );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
cache_dir.mkdir();
|
||||||
|
|
||||||
|
final File temp_file = new File( cache_dir, "tmp." + Thread.currentThread().getId() );
|
||||||
|
FileOutputStream os = new FileOutputStream( temp_file );
|
||||||
|
try{
|
||||||
|
if( is_jpeg ){
|
||||||
|
bitmap.compress( Bitmap.CompressFormat.JPEG, 95, os );
|
||||||
|
}else{
|
||||||
|
bitmap.compress( Bitmap.CompressFormat.PNG, 100, os );
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InputStreamOpener() {
|
||||||
|
@Override public InputStream open() throws IOException{
|
||||||
|
return new FileInputStream( temp_file );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String getMimeType(){
|
||||||
|
return mime_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void deleteTempFile(){
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
temp_file.delete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}finally{
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch( Throwable ex ){
|
||||||
|
log.trace( ex );
|
||||||
|
Utils.showToast( this, ex, "Resizing image failed." );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new InputStreamOpener() {
|
||||||
|
@Override public InputStream open() throws IOException{
|
||||||
|
return getContentResolver().openInputStream( uri );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String getMimeType(){
|
||||||
|
return mime_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void deleteTempFile(){
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAttachment( final int request_code, final Uri uri, final String mime_type ){
|
||||||
|
|
||||||
|
if( mime_type == null ){
|
||||||
|
Utils.showToast( this, false, "mime type is not provided." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! mime_type.startsWith( "image/" ) ){
|
||||||
|
Utils.showToast( this, false, "mime type is not image." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new AsyncTask< Void, Void, String >() {
|
||||||
|
|
||||||
|
@Override protected String doInBackground( Void... params ){
|
||||||
|
try{
|
||||||
|
final InputStreamOpener opener = createOpener( uri, mime_type );
|
||||||
|
try{
|
||||||
|
InputStream is = opener.open();
|
||||||
|
try{
|
||||||
|
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||||
|
//
|
||||||
|
bao.write( Utils.encodeUTF8( "data:" + opener.getMimeType() + ";base64," ) );
|
||||||
|
//
|
||||||
|
Base64OutputStream base64 = new Base64OutputStream( bao, Base64.NO_WRAP );
|
||||||
|
try{
|
||||||
|
IOUtils.copy( is, base64 );
|
||||||
|
}finally{
|
||||||
|
base64.close();
|
||||||
|
}
|
||||||
|
String value = Utils.decodeUTF8( bao.toByteArray() );
|
||||||
|
|
||||||
|
switch( request_code ){
|
||||||
|
case REQUEST_CODE_AVATAR_ATTACHMENT:
|
||||||
|
case REQUEST_CODE_AVATAR_CAMERA:
|
||||||
|
return "avatar=" + Uri.encode( value );
|
||||||
|
case REQUEST_CODE_HEADER_ATTACHMENT:
|
||||||
|
case REQUEST_CODE_HEADER_CAMERA:
|
||||||
|
return "header=" + Uri.encode( value );
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
IOUtils.closeQuietly( is );
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
opener.deleteTempFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch( Throwable ex ){
|
||||||
|
Utils.showToast( ActAccountSetting.this, ex, "image converting failed." );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute( String form_data ){
|
||||||
|
if( form_data != null ){
|
||||||
|
updateCredential( form_data );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}.executeOnExecutor( App1.task_executor );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1323,10 +1323,10 @@ class Column implements StreamReader.Callback {
|
||||||
return getStatuses( client, PATH_FEDERATE );
|
return getStatuses( client, PATH_FEDERATE );
|
||||||
|
|
||||||
case TYPE_PROFILE:
|
case TYPE_PROFILE:
|
||||||
if( who_account == null ){
|
|
||||||
parseAccount1( client, String.format( Locale.JAPAN, PATH_ACCOUNT, profile_id ) );
|
parseAccount1( client, String.format( Locale.JAPAN, PATH_ACCOUNT, profile_id ) );
|
||||||
client.callback.publishApiProgress( "" );
|
client.callback.publishApiProgress( "" );
|
||||||
}
|
|
||||||
switch( profile_tab ){
|
switch( profile_tab ){
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -28,6 +28,7 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
private final ImageButton btnFollow;
|
private final ImageButton btnFollow;
|
||||||
private final ImageView ivFollowedBy;
|
private final ImageView ivFollowedBy;
|
||||||
private final View llProfile;
|
private final View llProfile;
|
||||||
|
private final TextView tvRemoteProfileWarning;
|
||||||
|
|
||||||
private TootAccount who;
|
private TootAccount who;
|
||||||
|
|
||||||
|
@ -49,13 +50,15 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
View btnMore = viewRoot.findViewById( R.id.btnMore );
|
View btnMore = viewRoot.findViewById( R.id.btnMore );
|
||||||
btnFollow = (ImageButton) viewRoot.findViewById( R.id.btnFollow );
|
btnFollow = (ImageButton) viewRoot.findViewById( R.id.btnFollow );
|
||||||
ivFollowedBy = (ImageView) viewRoot.findViewById( R.id.ivFollowedBy );
|
ivFollowedBy = (ImageView) viewRoot.findViewById( R.id.ivFollowedBy );
|
||||||
|
tvRemoteProfileWarning = (TextView) viewRoot.findViewById( R.id.tvRemoteProfileWarning );
|
||||||
|
|
||||||
ivBackground.setOnClickListener( this );
|
ivBackground.setOnClickListener( this );
|
||||||
btnFollowing.setOnClickListener( this );
|
btnFollowing.setOnClickListener( this );
|
||||||
btnFollowers.setOnClickListener( this );
|
btnFollowers.setOnClickListener( this );
|
||||||
btnStatusCount.setOnClickListener( this );
|
btnStatusCount.setOnClickListener( this );
|
||||||
btnMore.setOnClickListener( this );
|
btnMore.setOnClickListener( this );
|
||||||
btnFollow.setOnClickListener( this );
|
btnFollow.setOnClickListener( this );
|
||||||
|
tvRemoteProfileWarning.setOnClickListener( this );
|
||||||
|
|
||||||
btnFollow.setOnLongClickListener( this );
|
btnFollow.setOnLongClickListener( this );
|
||||||
|
|
||||||
|
@ -77,6 +80,7 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
|
|
||||||
showColor();
|
showColor();
|
||||||
|
|
||||||
|
|
||||||
if( who == null ){
|
if( who == null ){
|
||||||
tvCreated.setText( "" );
|
tvCreated.setText( "" );
|
||||||
ivBackground.setImageDrawable( null );
|
ivBackground.setImageDrawable( null );
|
||||||
|
@ -89,11 +93,14 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + "?" );
|
btnFollowers.setText( activity.getString( R.string.followers ) + "\n" + "?" );
|
||||||
|
|
||||||
btnFollow.setImageDrawable( null );
|
btnFollow.setImageDrawable( null );
|
||||||
|
tvRemoteProfileWarning.setVisibility( View.GONE );
|
||||||
}else{
|
}else{
|
||||||
tvCreated.setText( TootStatus.formatTime( who.time_created_at ) );
|
tvCreated.setText( TootStatus.formatTime( who.time_created_at ) );
|
||||||
ivBackground.setImageUrl( activity.pref, 0f,access_info.supplyBaseUrl( who.header_static ) );
|
ivBackground.setImageUrl( activity.pref, 0f,access_info.supplyBaseUrl( who.header_static ) );
|
||||||
ivAvatar.setImageUrl( activity.pref, 16f,access_info.supplyBaseUrl( who.avatar_static ) , access_info.supplyBaseUrl( who.avatar ));
|
ivAvatar.setImageUrl( activity.pref, 16f,access_info.supplyBaseUrl( who.avatar_static ) , access_info.supplyBaseUrl( who.avatar ));
|
||||||
tvDisplayName.setText( who.decoded_display_name );
|
tvDisplayName.setText( who.decoded_display_name );
|
||||||
|
|
||||||
|
tvRemoteProfileWarning.setVisibility( column.access_info.isRemoteUser(who) ? View.VISIBLE : View.GONE );
|
||||||
|
|
||||||
String s = "@" + access_info.getFullAcct( who );
|
String s = "@" + access_info.getFullAcct( who );
|
||||||
if( who.locked ){
|
if( who.locked ){
|
||||||
|
@ -117,6 +124,7 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
switch( v.getId() ){
|
switch( v.getId() ){
|
||||||
|
|
||||||
case R.id.ivBackground:
|
case R.id.ivBackground:
|
||||||
|
case R.id.tvRemoteProfileWarning:
|
||||||
if( who != null ){
|
if( who != null ){
|
||||||
// 強制的にブラウザで開く
|
// 強制的にブラウザで開く
|
||||||
activity.openChromeTab( activity.nextPosition( column ), access_info, who.url, true );
|
activity.openChromeTab( activity.nextPosition( column ), access_info, who.url, true );
|
||||||
|
@ -152,6 +160,7 @@ class HeaderViewHolderProfile extends HeaderViewHolderBase implements View.OnCli
|
||||||
new DlgContextMenu( activity, column, who, null, null ).show();
|
new DlgContextMenu( activity, column, who, null, null ).show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.view.ViewCompat;
|
import android.support.v4.view.ViewCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -27,6 +29,7 @@ import jp.juggler.subwaytooter.table.ContentWarning;
|
||||||
import jp.juggler.subwaytooter.table.MediaShown;
|
import jp.juggler.subwaytooter.table.MediaShown;
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||||
import jp.juggler.subwaytooter.table.UserRelation;
|
import jp.juggler.subwaytooter.table.UserRelation;
|
||||||
|
import jp.juggler.subwaytooter.util.EmojiImageSpan;
|
||||||
import jp.juggler.subwaytooter.util.LogCategory;
|
import jp.juggler.subwaytooter.util.LogCategory;
|
||||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
|
import jp.juggler.subwaytooter.view.MyLinkMovementMethod;
|
||||||
import jp.juggler.subwaytooter.view.MyListView;
|
import jp.juggler.subwaytooter.view.MyListView;
|
||||||
|
@ -377,7 +380,21 @@ class ItemViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
llStatus.setVisibility( View.VISIBLE );
|
llStatus.setVisibility( View.VISIBLE );
|
||||||
|
|
||||||
tvTime.setText( TootStatus.formatTime( status.time_created_at ) );
|
if( status instanceof MSPToot ){
|
||||||
|
tvTime.setText( TootStatus.formatTime( status.time_created_at ) );
|
||||||
|
}else if( status instanceof TootStatus ){
|
||||||
|
TootStatus ts = (TootStatus) status;
|
||||||
|
int icon_id = Styler.getVisibilityIcon( activity, ts.visibility );
|
||||||
|
|
||||||
|
SpannableStringBuilder sb = new SpannableStringBuilder( );
|
||||||
|
int start = sb.length();
|
||||||
|
sb.append(ts.visibility);
|
||||||
|
int end = sb.length();
|
||||||
|
sb.setSpan( new EmojiImageSpan( activity, icon_id ), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE );
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(TootStatus.formatTime( status.time_created_at ));
|
||||||
|
tvTime.setText(sb);
|
||||||
|
}
|
||||||
|
|
||||||
account_thumbnail = status.account;
|
account_thumbnail = status.account;
|
||||||
setAcct( tvAcct, access_info.getFullAcct( status.account ) );
|
setAcct( tvAcct, access_info.getFullAcct( status.account ) );
|
||||||
|
|
|
@ -464,6 +464,24 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
||||||
return "?@?";
|
return "?@?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLocalUser( @NonNull TootAccount who ){
|
||||||
|
return isLocalUser(who.acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLocalUser( @NonNull String acct ){
|
||||||
|
int delm = acct.indexOf( '@' );
|
||||||
|
if( delm == -1 ) return true;
|
||||||
|
return host.equalsIgnoreCase( acct.substring( delm+1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoteUser( @NonNull TootAccount who ){
|
||||||
|
return ! isLocalUser(who);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoteUser( @NonNull String acct ){
|
||||||
|
return ! isLocalUser(acct);
|
||||||
|
}
|
||||||
|
|
||||||
public String getUserUrl( @NonNull String who_acct ){
|
public String getUserUrl( @NonNull String who_acct ){
|
||||||
int p = who_acct.indexOf( '@' );
|
int p = who_acct.indexOf( '@' );
|
||||||
if( - 1 != p ){
|
if( - 1 != p ){
|
||||||
|
@ -581,4 +599,5 @@ public class SavedAccount extends TootAccount implements LinkClickContext {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import android.text.style.ReplacementSpan;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
class EmojiImageSpan extends ReplacementSpan {
|
public class EmojiImageSpan extends ReplacementSpan {
|
||||||
|
|
||||||
static DynamicDrawableSpan x = null;
|
static DynamicDrawableSpan x = null;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class EmojiImageSpan extends ReplacementSpan {
|
||||||
private final int res_id;
|
private final int res_id;
|
||||||
private WeakReference< Drawable > mDrawableRef;
|
private WeakReference< Drawable > mDrawableRef;
|
||||||
|
|
||||||
EmojiImageSpan( @NonNull Context context, int res_id ){
|
public EmojiImageSpan( @NonNull Context context, int res_id ){
|
||||||
super();
|
super();
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.res_id = res_id;
|
this.res_id = res_id;
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class MyNetworkImageView extends AppCompatImageView {
|
||||||
|
|
||||||
public void setDefaultImageResId( int defaultImage ){
|
public void setDefaultImageResId( int defaultImage ){
|
||||||
mDefaultImageId = defaultImage;
|
mDefaultImageId = defaultImage;
|
||||||
|
loadImageIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
// エラー時に表示するDrawableのリソースID
|
// エラー時に表示するDrawableのリソースID
|
||||||
|
@ -60,6 +61,7 @@ public class MyNetworkImageView extends AppCompatImageView {
|
||||||
|
|
||||||
public void setErrorImageResId( int errorImage ){
|
public void setErrorImageResId( int errorImage ){
|
||||||
mErrorImageId = errorImage;
|
mErrorImageId = errorImage;
|
||||||
|
loadImageIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角丸の半径。元画像の短辺に対する割合を指定するらしい
|
// 角丸の半径。元画像の短辺に対する割合を指定するらしい
|
||||||
|
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 648 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -2,15 +2,15 @@
|
||||||
<ScrollView
|
<ScrollView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/svContent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true"
|
|
||||||
|
|
||||||
android:scrollbarStyle="outsideOverlay"
|
|
||||||
android:id="@+id/svContent"
|
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingBottom="128dp"
|
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingBottom="128dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:scrollbarStyle="outsideOverlay"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -60,25 +60,128 @@
|
||||||
style="@style/setting_row_label"
|
style="@style/setting_row_label"
|
||||||
android:text="@string/nickname_label"
|
android:text="@string/nickname_label"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LinearLayout style="@style/setting_row_form">
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvUserCustom"
|
android:id="@+id/tvUserCustom"
|
||||||
style="@style/setting_horizontal_stretch"
|
style="@style/setting_horizontal_stretch"
|
||||||
android:padding="4dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:padding="4dp"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/btnUserCustom"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:background="@drawable/btn_bg_transparent"
|
||||||
android:contentDescription="@string/edit"
|
android:contentDescription="@string/edit"
|
||||||
android:src="?attr/ic_edit"
|
android:src="?attr/ic_edit"
|
||||||
android:id="@+id/btnUserCustom"
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:text="@string/public_profile"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
<FrameLayout
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||||
|
android:id="@+id/ivProfileHeader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||||
|
android:id="@+id/ivProfileAvatar"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
/>
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnProfileAvatar"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/change_avatar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnProfileHeader"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:text="@string/change_header"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:labelFor="@+id/etDisplayName"
|
||||||
|
android:text="@string/display_name"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etDisplayName"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:inputType="text"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnDisplayName"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
android:background="@drawable/btn_bg_transparent"
|
android:background="@drawable/btn_bg_transparent"
|
||||||
|
android:contentDescription="@string/post"
|
||||||
|
android:src="?attr/btn_post"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:labelFor="@+id/etNote"
|
||||||
|
android:text="@string/note"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etNote"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnNote"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:background="@drawable/btn_bg_transparent"
|
||||||
|
android:contentDescription="@string/post"
|
||||||
|
android:src="?attr/btn_post"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -112,6 +215,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout style="@style/setting_row_form">
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -123,6 +227,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout style="@style/setting_row_form">
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -153,5 +153,13 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="@string/remote_profile_warning"
|
||||||
|
android:id="@+id/tvRemoteProfileWarning"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/btn_bg_transparent"
|
||||||
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -414,8 +414,14 @@
|
||||||
<string name="enable_gif_animation">Enable GIF animation (It wastes the battery very much)</string>
|
<string name="enable_gif_animation">Enable GIF animation (It wastes the battery very much)</string>
|
||||||
<string name="acct_sample">(sample)username@instance</string>
|
<string name="acct_sample">(sample)username@instance</string>
|
||||||
<string name="mention_full_acct">Show mention as full acct (app restart required)</string>
|
<string name="mention_full_acct">Show mention as full acct (app restart required)</string>
|
||||||
|
<string name="remote_profile_warning">Remote users\' profile may be inadequate information. You can check more accurate information on the web page.</string>
|
||||||
|
<string name="public_profile">Public profile</string>
|
||||||
|
<string name="change_avatar">Change avatar icon</string>
|
||||||
|
<string name="change_header">Change header image</string>
|
||||||
|
<string name="display_name">Display name</string>
|
||||||
|
<string name="note">Note</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>-->
|
||||||
<!--<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$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>-->
|
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->
|
||||||
|
|
|
@ -701,5 +701,11 @@
|
||||||
<string name="acct_sample">(見本)username@instance</string>
|
<string name="acct_sample">(見本)username@instance</string>
|
||||||
<string name="enable_gif_animation">GIFアニメーションを有効にする(バッテリーをとても浪費します)</string>
|
<string name="enable_gif_animation">GIFアニメーションを有効にする(バッテリーをとても浪費します)</string>
|
||||||
<string name="mention_full_acct">メンションのインスタンス名部分を省略しない(アプリ再起動が必要)</string>
|
<string name="mention_full_acct">メンションのインスタンス名部分を省略しない(アプリ再起動が必要)</string>
|
||||||
|
<string name="remote_profile_warning">リモートユーザのプロフィールは情報が不十分な可能性があります。より正確な情報をWebページで確認することができます。</string>
|
||||||
|
<string name="change_avatar">アバター画像を変更</string>
|
||||||
|
<string name="change_header">ヘッダー画像を変更</string>
|
||||||
|
<string name="display_name">表示名</string>
|
||||||
|
<string name="note">ノート</string>
|
||||||
|
<string name="public_profile">公開プロフィール</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -117,5 +117,6 @@
|
||||||
<attr name="ic_domain_block" format="reference" />
|
<attr name="ic_domain_block" format="reference" />
|
||||||
|
|
||||||
<attr name="ic_nicoru" format="reference" />
|
<attr name="ic_nicoru" format="reference" />
|
||||||
|
<attr name="ic_question" format="reference" />
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -409,5 +409,11 @@
|
||||||
<string name="enable_gif_animation">Enable GIF animation (It wastes the battery very much)</string>
|
<string name="enable_gif_animation">Enable GIF animation (It wastes the battery very much)</string>
|
||||||
<string name="acct_sample">(sample)username@instance</string>
|
<string name="acct_sample">(sample)username@instance</string>
|
||||||
<string name="mention_full_acct">Show mention as full acct (app restart required)</string>
|
<string name="mention_full_acct">Show mention as full acct (app restart required)</string>
|
||||||
|
<string name="remote_profile_warning">Remote users\' profile may be inadequate information. You can check more accurate information on the web page.</string>
|
||||||
|
<string name="public_profile">Public profile</string>
|
||||||
|
<string name="change_avatar">Change avatar icon</string>
|
||||||
|
<string name="change_header">Change header image</string>
|
||||||
|
<string name="display_name">Display name</string>
|
||||||
|
<string name="note">Note</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
<item name="ic_domain_block">@drawable/ic_domain_block</item>
|
<item name="ic_domain_block">@drawable/ic_domain_block</item>
|
||||||
|
|
||||||
<item name="ic_nicoru">@drawable/ic_nicoru</item>
|
<item name="ic_nicoru">@drawable/ic_nicoru</item>
|
||||||
|
<item name="ic_question">@drawable/ic_question</item>
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -179,6 +180,7 @@
|
||||||
<item name="ic_domain_block">@drawable/ic_domain_block_dark</item>
|
<item name="ic_domain_block">@drawable/ic_domain_block_dark</item>
|
||||||
|
|
||||||
<item name="ic_nicoru">@drawable/ic_nicoru_dark</item>
|
<item name="ic_nicoru">@drawable/ic_nicoru_dark</item>
|
||||||
|
<item name="ic_question">@drawable/ic_question_dark</item>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|