画面下部にカラム一覧。カラム設定に色と背景。他タンスのハッシュタグを疑似アカウントで開く

This commit is contained in:
tateisu 2017-05-08 03:16:41 +09:00
parent e8529e4c1b
commit 23c5c9d572
24 changed files with 1660 additions and 649 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "jp.juggler.subwaytooter"
minSdkVersion 21
targetSdkVersion 25
versionCode 39
versionName "0.3.9"
versionCode 40
versionName "0.4.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

View File

@ -130,8 +130,13 @@
<activity
android:name=".ActMutedApp"
android:label="@string/muted_app"
/>
<activity
android:name=".ActColumnCustomize"
android:label="@string/color_and_background"
/>
<activity
android:name=".ActNickname"
android:label="@string/nickname_and_color"

View File

@ -375,7 +375,7 @@ public class ActAccountSetting extends AppCompatActivity
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor(App1.task_executor);
}
}

View File

@ -0,0 +1,309 @@
package jp.juggler.subwaytooter;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import com.jrummyapps.android.colorpicker.ColorPickerDialog;
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
public class ActColumnCustomize extends AppCompatActivity
implements View.OnClickListener, ColorPickerDialogListener
{
static final LogCategory log = new LogCategory( "ActColumnCustomize" );
static final String EXTRA_COLUMN_INDEX = "column_index";
public static void open( ActMain activity, int idx, int request_code ){
Intent intent = new Intent( activity, ActColumnCustomize.class );
intent.putExtra( EXTRA_COLUMN_INDEX, idx );
activity.startActivityForResult( intent, request_code );
}
@Override public void onBackPressed(){
makeResult();
super.onBackPressed();
}
private void makeResult(){
Intent data = new Intent();
data.putExtra( EXTRA_COLUMN_INDEX, column_index );
setResult( RESULT_OK ,data);
}
int column_index;
Column column;
AppState app_state;
float density;
@Override protected void onCreate( @Nullable Bundle savedInstanceState ){
super.onCreate( savedInstanceState );
App1.setActivityTheme( this, false );
initUI();
app_state = App1.getAppState( this );
density = app_state.density;
column_index = getIntent().getIntExtra( EXTRA_COLUMN_INDEX, 0 );
column = app_state.column_list.get( column_index );
show();
}
@Override protected void onDestroy(){
closeBitmaps();
super.onDestroy();
}
static final int COLOR_DIALOG_ID_HEADER_BACKGROUND = 1;
static final int COLOR_DIALOG_ID_HEADER_FOREGROUND = 2;
static final int COLOR_DIALOG_ID_COLUMN_BACKGROUND = 3;
@Override public void onClick( View v ){
ColorPickerDialog.Builder builder;
switch( v.getId() ){
case R.id.btnHeaderBackgroundEdit:
builder = ColorPickerDialog.newBuilder()
.setDialogType( ColorPickerDialog.TYPE_CUSTOM )
.setAllowPresets( true )
.setShowAlphaSlider( false )
.setDialogId( COLOR_DIALOG_ID_HEADER_BACKGROUND )
;
if( column.header_bg_color != 0 ) builder.setColor( column.header_bg_color );
builder.show( this );
break;
case R.id.btnHeaderBackgroundReset:
column.header_bg_color = 0;
show();
break;
case R.id.btnHeaderTextEdit:
builder = ColorPickerDialog.newBuilder()
.setDialogType( ColorPickerDialog.TYPE_CUSTOM )
.setAllowPresets( true )
.setShowAlphaSlider( false )
.setDialogId( COLOR_DIALOG_ID_HEADER_FOREGROUND )
;
if( column.header_fg_color != 0 ) builder.setColor( column.header_fg_color );
builder.show( this );
break;
case R.id.btnHeaderTextReset:
column.header_fg_color = 0;
show();
break;
case R.id.btnColumnBackgroundColor:
builder = ColorPickerDialog.newBuilder()
.setDialogType( ColorPickerDialog.TYPE_CUSTOM )
.setAllowPresets( true )
.setShowAlphaSlider( false )
.setDialogId( COLOR_DIALOG_ID_COLUMN_BACKGROUND )
;
if( column.column_bg_color != 0 ) builder.setColor( column.column_bg_color );
builder.show( this );
break;
case R.id.btnColumnBackgroundColorReset:
column.column_bg_color = 0;
show();
break;
case R.id.btnColumnBackgroundImage:
Intent intent = new Intent( Intent.ACTION_OPEN_DOCUMENT );
intent.addCategory( Intent.CATEGORY_OPENABLE );
intent.setType( "image/*" );
startActivityForResult( intent, REQUEST_CODE_PICK_BACKGROUND );
break;
case R.id.btnColumnBackgroundImageReset:
column.column_bg_image = null;
show();
break;
}
}
@Override public void onColorSelected( int dialogId, @ColorInt int color ){
switch( dialogId ){
case COLOR_DIALOG_ID_HEADER_BACKGROUND:
column.header_bg_color = 0xff000000 | color;
break;
case COLOR_DIALOG_ID_HEADER_FOREGROUND:
column.header_fg_color = 0xff000000 | color;
break;
case COLOR_DIALOG_ID_COLUMN_BACKGROUND:
column.column_bg_color = 0xff000000 | color;
break;
}
show();
}
@Override public void onDialogDismissed( int dialogId ){
}
static final int REQUEST_CODE_PICK_BACKGROUND = 1;
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){
if( requestCode == REQUEST_CODE_PICK_BACKGROUND && resultCode == RESULT_OK ){
if( data != null ){
Uri uri = data.getData();
if( uri != null ){
getContentResolver().takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION );
column.column_bg_image = uri.toString();
show();
}
}
}
}
View flColumnBackground;
ImageView ivColumnBackground;
SeekBar sbColumnBackgroundAlpha;
View llColumnHeader;
ImageView ivColumnHeader;
TextView tvColumnName;
static final int PROGRESS_MAX = 65536;
private void initUI(){
setContentView( R.layout.act_column_customize );
findViewById( R.id.btnHeaderBackgroundEdit ).setOnClickListener( this );
findViewById( R.id.btnHeaderBackgroundReset ).setOnClickListener( this );
findViewById( R.id.btnHeaderTextEdit ).setOnClickListener( this );
findViewById( R.id.btnHeaderTextReset ).setOnClickListener( this );
findViewById( R.id.btnColumnBackgroundColor ).setOnClickListener( this );
findViewById( R.id.btnColumnBackgroundColorReset ).setOnClickListener( this );
findViewById( R.id.btnColumnBackgroundImage ).setOnClickListener( this );
findViewById( R.id.btnColumnBackgroundImageReset ).setOnClickListener( this );
llColumnHeader = findViewById( R.id.llColumnHeader );
ivColumnHeader = (ImageView) findViewById( R.id.ivColumnHeader );
tvColumnName = (TextView) findViewById( R.id.tvColumnName );
flColumnBackground = findViewById( R.id.flColumnBackground );
ivColumnBackground = (ImageView) findViewById( R.id.ivColumnBackground );
sbColumnBackgroundAlpha = (SeekBar) findViewById( R.id.sbColumnBackgroundAlpha );
sbColumnBackgroundAlpha.setMax( PROGRESS_MAX );
sbColumnBackgroundAlpha.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {
@Override public void onStartTrackingTouch( SeekBar seekBar ){
}
@Override public void onStopTrackingTouch( SeekBar seekBar ){
}
@Override
public void onProgressChanged( SeekBar seekBar, int progress, boolean fromUser ){
if( fromUser ){
column.column_bg_image_alpha = progress / (float)PROGRESS_MAX;
ivColumnBackground.setAlpha( column.column_bg_image_alpha );
}
}
} );
}
private void show(){
int c = column.header_bg_color;
if( c == 0 ){
llColumnHeader.setBackgroundResource( R.drawable.btn_bg_ddd );
}else{
ViewCompat.setBackground( llColumnHeader,Styler.getAdaptiveRippleDrawable(
c,
(column.header_fg_color != 0 ? column.header_fg_color :
Styler.getAttributeColor( this,R.attr.colorRippleEffect ))
) );
}
c = column.header_fg_color;
if( c == 0 ){
tvColumnName.setTextColor( Styler.getAttributeColor( this, android.R.attr.textColorPrimary ) );
Styler.setIconDefaultColor( this, ivColumnHeader, Column.getIconAttrId( column.column_type ) );
}else{
tvColumnName.setTextColor( c );
Styler.setIconCustomColor( this, ivColumnHeader, c, Column.getIconAttrId( column.column_type ) );
}
tvColumnName.setText( column.getColumnName( false ));
if( column.column_bg_color != 0 ){
flColumnBackground.setBackgroundColor( column.column_bg_color );
}else{
ViewCompat.setBackground( flColumnBackground, null );
}
float alpha =column.column_bg_image_alpha;
if( Float.isNaN( alpha )){
alpha = column.column_bg_image_alpha = 1f;
}
ivColumnBackground.setAlpha( alpha );
sbColumnBackgroundAlpha.setProgress( (int) ( 0.5f + alpha * PROGRESS_MAX ) );
loadImage( ivColumnBackground, column.column_bg_image );
}
String last_image_uri;
Bitmap last_image_bitmap;
private void closeBitmaps(){
try{
ivColumnBackground.setImageDrawable( null );
last_image_uri = null;
if( last_image_bitmap != null ){
last_image_bitmap.recycle();
last_image_bitmap = null;
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}
private void loadImage( ImageView ivColumnBackground, String url ){
try{
if( TextUtils.isEmpty(url ) ){
closeBitmaps();
return;
}else if( url.equals( last_image_uri ) ){
// 今表示してるのと同じ
return;
}
// 直前のBitmapを掃除する
closeBitmaps();
// 画像をロードして成功したら表示してURLを覚える
int resize_max = (int) ( 0.5f + 64f * density );
Uri uri = Uri.parse( url );
last_image_bitmap = Utils.createResizedBitmap( log, this, uri,false, resize_max );
if( last_image_bitmap != null ){
ivColumnBackground.setImageBitmap( last_image_bitmap );
last_image_uri = url;
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}
}

View File

@ -12,7 +12,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.customtabs.CustomTabsIntent;
import android.support.v4.os.AsyncTaskCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
@ -25,8 +25,10 @@ import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -59,7 +61,7 @@ import okhttp3.Request;
import okhttp3.RequestBody;
public class ActMain extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener
implements NavigationView.OnNavigationItemSelectedListener, View.OnClickListener, ViewPager.OnPageChangeListener
{
public static final LogCategory log = new LogCategory( "ActMain" );
@ -93,11 +95,13 @@ public class ActMain extends AppCompatActivity
this.density = app_state.density;
this.acct_pad_lr = (int) ( 0.5f + 4f * density );
initUI();
updateColumnStrip();
if( pager_adapter.column_list.size() > 0 ){
llEmpty.setVisibility( View.GONE );
onPageSelected( pager.getCurrentItem() );
}
AlarmService.startCheck( this );
@ -129,6 +133,7 @@ public class ActMain extends AppCompatActivity
if( bRemoved ){
pager_adapter.setOrder( pager, new_order );
app_state.saveColumnList();
updateColumnStrip();
}
}
@ -165,7 +170,7 @@ public class ActMain extends AppCompatActivity
private void handleSentIntent( final Intent sent_intent ){
AccountPicker.pick( this, false, true, getString( R.string.account_picker_toot ), new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
ActPost.open( ActMain.this,REQUEST_CODE_POST,ai.db_id,sent_intent);
ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, sent_intent );
}
} );
}
@ -189,6 +194,44 @@ public class ActMain extends AppCompatActivity
super.onPause();
}
@Override public void onClick( View v ){
switch( v.getId() ){
case R.id.btnMenu:
if( ! drawer.isDrawerOpen( Gravity.START ) ){
drawer.openDrawer( Gravity.START );
}
break;
case R.id.btnToot:
performTootButton();
break;
}
}
@Override
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels ){
}
@Override public void onPageSelected( final int position ){
handler.post( new Runnable() {
@Override public void run(){
if( position >= 0 && position < app_state.column_list.size() ){
Column column = app_state.column_list.get( position );
if( ! column.bFirstInitialized ){
column.startLoading();
}
scrollColumnStrip( position );
}
}
} );
}
@Override public void onPageScrollStateChanged( int state ){
}
boolean isOrderChanged( ArrayList< Integer > new_order ){
if( new_order.size() != pager_adapter.getCount() ) return true;
for( int i = 0, ie = new_order.size() ; i < ie ; ++ i ){
@ -202,6 +245,7 @@ public class ActMain extends AppCompatActivity
static final int REQUEST_APP_ABOUT = 3;
static final int REQUEST_CODE_NICKNAME = 4;
static final int REQUEST_CODE_POST = 5;
static final int REQUEST_COLUMN_COLOR = 6;
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data ){
log.d( "onActivityResult" );
@ -212,6 +256,7 @@ public class ActMain extends AppCompatActivity
if( order != null && isOrderChanged( order ) ){
pager_adapter.setOrder( pager, order );
app_state.saveColumnList();
updateColumnStrip();
}
if( pager_adapter.column_list.isEmpty() ){
@ -220,6 +265,7 @@ public class ActMain extends AppCompatActivity
int select = data.getIntExtra( ActColumnList.EXTRA_SELECTION, - 1 );
if( select != - 1 ){
pager.setCurrentItem( select, true );
scrollColumnStrip( select );
}
}
}
@ -245,6 +291,16 @@ public class ActMain extends AppCompatActivity
posted_acct = data.getStringExtra( ActPost.EXTRA_POSTED_ACCT );
posted_status_id = data.getLongExtra( ActPost.EXTRA_POSTED_STATUS_ID, 0L );
}
}else if( requestCode == REQUEST_COLUMN_COLOR ){
if( data != null ){
app_state.saveColumnList();
int idx = data.getIntExtra( ActColumnCustomize.EXTRA_COLUMN_INDEX, 0 );
ColumnViewHolder vh = pager_adapter.getColumnViewHolder( idx );
if( vh != null ){
vh.showColumnColor();
}
updateColumnStrip();
}
}
}
super.onActivityResult( requestCode, resultCode, data );
@ -419,16 +475,21 @@ public class ActMain extends AppCompatActivity
ViewPager pager;
ColumnPagerAdapter pager_adapter;
View llEmpty;
DrawerLayout drawer;
LinearLayout llColumnStrip;
HorizontalScrollView svColumnStrip;
void initUI(){
setContentView( R.layout.act_main );
llEmpty = findViewById( R.id.llEmpty );
// // toolbar
// Toolbar toolbar = (Toolbar) findViewById( R.id.toolbar );
// setSupportActionBar( toolbar );
// navigation drawer
final DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
// ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
// this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close );
// drawer.addDrawerListener( toggle );
@ -437,30 +498,109 @@ public class ActMain extends AppCompatActivity
NavigationView navigationView = (NavigationView) findViewById( R.id.nav_view );
navigationView.setNavigationItemSelectedListener( this );
// floating action button
View fabToot = (View) findViewById( R.id.fabToot );
fabToot.setOnClickListener( new View.OnClickListener() {
@Override public void onClick( View view ){
performTootButton();
}
} );
// floating action button
View fabMenu = findViewById( R.id.fabMenu );
fabMenu.setOnClickListener( new View.OnClickListener() {
@Override public void onClick( View view ){
if( ! drawer.isDrawerOpen( Gravity.START ) ){
drawer.openDrawer( Gravity.START );
}
}
} );
View v;
v = findViewById( R.id.btnToot );
v.setOnClickListener( this );
v = findViewById( R.id.btnMenu );
v.setOnClickListener( this );
llColumnStrip = (LinearLayout) findViewById( R.id.llColumnStrip );
svColumnStrip = (HorizontalScrollView) findViewById( R.id.svColumnStrip );
svColumnStrip.setHorizontalFadingEdgeEnabled( true );
// ViewPager
pager = (ViewPager) findViewById( R.id.viewPager );
pager_adapter = new ColumnPagerAdapter( this );
pager.setAdapter( pager_adapter );
pager.addOnPageChangeListener( this );
}
void updateColumnStrip(){
llColumnStrip.removeAllViews();
for( int i = 0, ie = app_state.column_list.size() ; i < ie ; ++ i ){
final Column column = app_state.column_list.get( i );
View viewRoot = getLayoutInflater().inflate( R.layout.lv_column_strip, llColumnStrip, false );
ImageView ivIcon = (ImageView) viewRoot.findViewById( R.id.ivIcon );
viewRoot.setTag( i );
viewRoot.setOnClickListener( new View.OnClickListener() {
@Override public void onClick( View v ){
onClickColumnStrip( (Integer) v.getTag() );
}
} );
viewRoot.setContentDescription( column.getColumnName( true ) );
//
int c = column.header_bg_color;
if( c == 0 ){
viewRoot.setBackgroundResource( R.drawable.btn_bg_ddd );
}else{
ViewCompat.setBackground( viewRoot,Styler.getAdaptiveRippleDrawable(
c,
(column.header_fg_color != 0 ? column.header_fg_color :
Styler.getAttributeColor( this,R.attr.colorRippleEffect ))
) );
}
c = column.header_fg_color;
if( c == 0 ){
Styler.setIconDefaultColor( this, ivIcon, Column.getIconAttrId( column.column_type ) );
}else{
Styler.setIconCustomColor( this, ivIcon, c, Column.getIconAttrId( column.column_type ) );
}
//
AcctColor ac = AcctColor.load( column.access_info.acct );
if( AcctColor.hasColorForeground( ac ) ){
View vAcctColor = viewRoot.findViewById( R.id.vAcctColor );
vAcctColor.setBackgroundColor( ac.color_fg );
}
//
llColumnStrip.addView( viewRoot );
//
}
svColumnStrip.requestLayout();
}
private void scrollColumnStrip( final int select ){
int child_count = llColumnStrip.getChildCount();
if( select < 0 || select >= child_count ){
return;
}
View icon = llColumnStrip.getChildAt( select );
int sv_width = ( (View) llColumnStrip.getParent() ).getWidth();
int ll_width = llColumnStrip.getWidth();
int icon_width = icon.getWidth();
int icon_left = icon.getLeft();
if( sv_width == 0 || ll_width == 0 || icon_width == 0 ){
handler.postDelayed( new Runnable() {
@Override public void run(){
scrollColumnStrip( select );
}
}, 20L );
}
int sx = icon_left + icon_width / 2 - sv_width / 2;
svColumnStrip.smoothScrollTo( sx, 0 );
}
private void onClickColumnStrip( int idx ){
pager.setCurrentItem( idx, true );
}
public void performAccountAdd(){
LoginForm.showLoginForm( this, null, new LoginForm.LoginFormCallback() {
@Override
@ -520,14 +660,14 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, true, sv );
log.e( result.error );
}else{
SavedAccount a = addPseudoAccount(instance);
SavedAccount a = addPseudoAccount( instance );
if( a != null ){
// 疑似アカウントが追加された
Utils.showToast( ActMain.this, false, R.string.server_confirmed );
addColumn( a, Column.TYPE_LOCAL );
dialog.dismiss();
}
}
}
};
@ -540,13 +680,13 @@ public class ActMain extends AppCompatActivity
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor( App1.task_executor );
}
} );
}
SavedAccount addPseudoAccount(String host){
SavedAccount addPseudoAccount( String host ){
try{
String username = "?";
String full_acct = username + "@" + host;
@ -557,8 +697,8 @@ public class ActMain extends AppCompatActivity
long row_id = SavedAccount.insert( host, full_acct, account_info, new JSONObject() );
SavedAccount account = SavedAccount.loadAccount( log, row_id );
if( account == null){
throw new RuntimeException( "loadAccount returns null.");
if( account == null ){
throw new RuntimeException( "loadAccount returns null." );
}
account.notification_follow = false;
account.notification_favourite = false;
@ -568,8 +708,8 @@ public class ActMain extends AppCompatActivity
return account;
}catch( JSONException ex ){
ex.printStackTrace();
log.e(ex,"addPseudoAccount failed.");
Utils.showToast( this, ex,"addPseudoAccount failed.");
log.e( ex, "addPseudoAccount failed." );
Utils.showToast( this, ex, "addPseudoAccount failed." );
}
return null;
}
@ -598,35 +738,35 @@ public class ActMain extends AppCompatActivity
// https://mastodon.juggler.jp/@SubwayTooter
final String host = m.group( 1 );
final String user = Uri.decode( m.group( 2 ) );
ArrayList<SavedAccount> account_list = SavedAccount.loadAccountList( log );
ArrayList<SavedAccount> account_list_same_host = new ArrayList<>( );
ArrayList< SavedAccount > account_list = SavedAccount.loadAccountList( log );
ArrayList< SavedAccount > account_list_same_host = new ArrayList<>();
for( SavedAccount a : account_list){
for( SavedAccount a : account_list ){
if( host.equalsIgnoreCase( a.host ) ){
account_list_same_host.add(a);
account_list_same_host.add( a );
}
}
if( account_list_same_host.isEmpty() ){
account_list_same_host.add( addPseudoAccount( host ));
account_list_same_host.add( addPseudoAccount( host ) );
}
AccountPicker.pick( this, true, true
,getString(R.string.account_picker_open_user_who,user+"@"+host)
,account_list_same_host
, getString( R.string.account_picker_open_user_who, user + "@" + host )
, account_list_same_host
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( final SavedAccount ai ){
startGetAccount( ai, host, user, new GetAccountCallback() {
@Override public void onGetAccount( TootAccount who ){
if( who != null ){
performOpenUser( ai, who );
return;
@Override public void onAccountPicked( final SavedAccount ai ){
startGetAccount( ai, host, user, new GetAccountCallback() {
@Override public void onGetAccount( TootAccount who ){
if( who != null ){
performOpenUser( ai, who );
return;
}
openChromeTab( ai, uri.toString(), true );
}
openChromeTab( ai, uri.toString(), true );
}
} );
}
} );
} );
}
} );
return;
}
return;
@ -800,7 +940,7 @@ public class ActMain extends AppCompatActivity
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor( App1.task_executor );
}
void reloadAccountSetting(){
@ -849,10 +989,12 @@ public class ActMain extends AppCompatActivity
int page_delete = pager_adapter.column_list.indexOf( column );
pager_adapter.removeColumn( pager, column );
app_state.saveColumnList();
updateColumnStrip();
if( pager_adapter.getCount() == 0 ){
llEmpty.setVisibility( View.VISIBLE );
}else if( page_showing > 0 && page_showing == page_delete ){
pager.setCurrentItem( page_showing - 1, true );
scrollColumnStrip( page_showing - 1 );
}
}
@ -870,9 +1012,10 @@ public class ActMain extends AppCompatActivity
//
llEmpty.setVisibility( View.GONE );
//
Column col = new Column( app_state , ai, type, params );
Column col = new Column( app_state, ai, type, params );
int idx = pager_adapter.addColumn( pager, col );
app_state.saveColumnList();
updateColumnStrip();
pager.setCurrentItem( idx, true );
return col;
}
@ -887,13 +1030,13 @@ public class ActMain extends AppCompatActivity
public void performOpenUserFromAnotherAccount( final TootAccount who, ArrayList< SavedAccount > account_list_non_pseudo_same_instance ){
AccountPicker.pick( this, false, false
,getString(R.string.account_picker_open_user_who,AcctColor.getNickname( who.acct) )
, getString( R.string.account_picker_open_user_who, AcctColor.getNickname( who.acct ) )
, account_list_non_pseudo_same_instance
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
addColumn( ai, Column.TYPE_PROFILE, who.id );
}
} );
@Override public void onAccountPicked( SavedAccount ai ){
addColumn( ai, Column.TYPE_PROFILE, who.id );
}
} );
}
public void performConversation( SavedAccount access_info, TootStatus status ){
@ -902,20 +1045,20 @@ public class ActMain extends AppCompatActivity
private void performAddTimeline( boolean bAllowPseudo, final int type, final Object... args ){
AccountPicker.pick( this, bAllowPseudo, true
,getString(R.string.account_picker_add_timeline_of,Column.getColumnTypeName(this,type))
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
switch( type ){
default:
addColumn( ai, type, args );
break;
case Column.TYPE_PROFILE:
addColumn( ai, type, ai.id );
break;
, getString( R.string.account_picker_add_timeline_of, Column.getColumnTypeName( this, type ) )
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
switch( type ){
default:
addColumn( ai, type, args );
break;
case Column.TYPE_PROFILE:
addColumn( ai, type, ai.id );
break;
}
}
}
} );
} );
}
public void openHashTag( SavedAccount access_info, String tag ){
@ -1004,7 +1147,7 @@ public class ActMain extends AppCompatActivity
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor( App1.task_executor );
}
static final Pattern reHashTag = Pattern.compile( "\\Ahttps://([^/]+)/tags/([^?#]+)\\z" );
@ -1075,7 +1218,7 @@ public class ActMain extends AppCompatActivity
}
// 他インスタンスのハッシュタグの表示
private void openHashTagOtherInstance( final SavedAccount access_info, final String url, String host, final String tag ){
private void openHashTagOtherInstance( final SavedAccount access_info, final String url, final String host, final String tag ){
ActionsDialog dialog = new ActionsDialog();
@ -1097,7 +1240,13 @@ public class ActMain extends AppCompatActivity
} );
// 各アカウントで開く選択肢
boolean has_host = false;
for( SavedAccount a : account_list ){
if( host.equalsIgnoreCase( a.host ) ){
has_host = true;
}
final SavedAccount _a = a;
dialog.addAction( getString( R.string.open_in_account, a.acct ), new Runnable() {
@Override public void run(){
@ -1106,7 +1255,16 @@ public class ActMain extends AppCompatActivity
} );
}
// TODO: もしカラならログイン無しアカウントで開きたいかも
if( ! has_host ){
dialog.addAction( getString( R.string.open_in_pseudo_account, "?@" + host ), new Runnable() {
@Override public void run(){
SavedAccount sa = addPseudoAccount( host );
if( sa != null ){
openHashTag( sa, tag );
}
}
} );
}
dialog.show( this, "#" + tag );
@ -1140,12 +1298,12 @@ public class ActMain extends AppCompatActivity
public void performMentionFromAnotherAccount( SavedAccount access_info, final TootAccount who, ArrayList< SavedAccount > account_list_non_pseudo ){
final String initial_text = "@" + access_info.getFullAcct( who ) + " ";
AccountPicker.pick( this, false, false
,getString(R.string.account_picker_toot )
, getString( R.string.account_picker_toot )
, account_list_non_pseudo, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, initial_text );
}
} );
@Override public void onAccountPicked( SavedAccount ai ){
ActPost.open( ActMain.this, REQUEST_CODE_POST, ai.db_id, initial_text );
}
} );
}
/////////////////////////////////////////////////////////////////////////
@ -1161,7 +1319,6 @@ public class ActMain extends AppCompatActivity
/////////////////////////////////////////////////////////////////////////
// favourite
public void performFavourite( final SavedAccount account, final TootStatus status, final RelationChangedCallback callback ){
//
final String busy_key = account.host + ":" + status.id;
@ -1255,14 +1412,14 @@ public class ActMain extends AppCompatActivity
showColumnMatchAccount( account );
}
}.execute();
}.executeOnExecutor( App1.task_executor );
// ファボ表示を更新中にする
showColumnMatchAccount( account );
}
/////////////////////////////////////////////////////////////////////////
// boost
public void performBoost( final SavedAccount access_info, final TootStatus status, boolean bConfirmed, final RelationChangedCallback callback ){
//
final String busy_key = access_info.host + ":" + status.id;
@ -1375,7 +1532,7 @@ public class ActMain extends AppCompatActivity
showColumnMatchAccount( access_info );
}
}.execute();
}.executeOnExecutor( App1.task_executor );
showColumnMatchAccount( access_info );
}
@ -1384,12 +1541,12 @@ public class ActMain extends AppCompatActivity
private void performAccountSetting(){
AccountPicker.pick( this, true, true
,getString(R.string.account_picker_open_setting)
, getString( R.string.account_picker_open_setting )
, new AccountPicker.AccountPickerCallback() {
@Override public void onAccountPicked( SavedAccount ai ){
ActAccountSetting.open( ActMain.this, ai, REQUEST_CODE_ACCOUNT_SETTING );
}
} );
@Override public void onAccountPicked( SavedAccount ai ){
ActAccountSetting.open( ActMain.this, ai, REQUEST_CODE_ACCOUNT_SETTING );
}
} );
}
private void performAppSetting(){
@ -1400,7 +1557,7 @@ public class ActMain extends AppCompatActivity
// column list
private void openColumnList(){
ActColumnList.open(this,pager.getCurrentItem(),REQUEST_CODE_COLUMN_LIST);
ActColumnList.open( this, pager.getCurrentItem(), REQUEST_CODE_COLUMN_LIST );
}
// private void dumpColumnList(){
@ -1410,8 +1567,6 @@ public class ActMain extends AppCompatActivity
// }
// }
////////////////////////////////////////////////////////////////////////////
interface RelationChangedCallback {
@ -1624,7 +1779,7 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
// acct で指定したユーザをリモートフォローする
@ -1743,7 +1898,7 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
////////////////////////////////////////
@ -1814,7 +1969,7 @@ public class ActMain extends AppCompatActivity
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
void callBlock( final SavedAccount access_info, final TootAccount who, final boolean bBlock, final RelationChangedCallback callback ){
@ -1881,7 +2036,7 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
void callFollowRequestAuthorize( final SavedAccount access_info
@ -1932,7 +2087,7 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
void deleteStatus( final SavedAccount access_info, final long status_id ){
@ -1977,7 +2132,7 @@ public class ActMain extends AppCompatActivity
Utils.showToast( ActMain.this, false, result.error );
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
interface ReportCompleteCallback {
@ -2034,7 +2189,7 @@ public class ActMain extends AppCompatActivity
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
void openReportForm( @NonNull final SavedAccount account, @NonNull final TootAccount who, @NonNull final TootStatus status ){
@ -2193,7 +2348,7 @@ public class ActMain extends AppCompatActivity
}
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
}

View File

@ -11,10 +11,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@ -26,7 +22,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.os.AsyncTaskCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
@ -41,7 +36,6 @@ import android.widget.ImageButton;
import android.widget.ScrollView;
import android.widget.TextView;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
@ -55,7 +49,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import it.sephiroth.android.library.exif2.ExifInterface;
import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootAttachment;
@ -316,6 +309,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
if( sent_intent != null ){
String action = sent_intent.getAction();
String type = sent_intent.getType();
//noinspection StatementWithEmptyBody
if( type == null ){
//
}else if( type.startsWith( "image/" ) ){
@ -781,7 +775,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
iv.setVisibility( View.GONE );
}else{
iv.setVisibility( View.VISIBLE );
iv.setCornerRadius( pref,16f );
iv.setCornerRadius( pref, 16f );
PostAttachment a = attachment_list.get( idx );
if( a.attachment != null && a.status == PostAttachment.ATTACHMENT_UPLOADED ){
iv.setImageUrl( a.attachment.preview_url, App1.getImageLoader() );
@ -870,175 +864,12 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
break;
}
// EXIF回転情報の取得
Integer orientation;
InputStream is = getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( this, false, "could not open image." );
break;
}
try{
ExifInterface exif = new ExifInterface();
exif.readExif( is, ExifInterface.Options.OPTION_IFD_0 | ExifInterface.Options.OPTION_IFD_1 | ExifInterface.Options.OPTION_IFD_EXIF );
orientation = exif.getTagIntValue( ExifInterface.TAG_ORIENTATION );
}finally{
IOUtils.closeQuietly( is );
}
// 画像のサイズを調べる
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inScaled = false;
is = getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( this, false, "could not open image." );
break;
}
try{
BitmapFactory.decodeStream( is, null, options );
}finally{
IOUtils.closeQuietly( is );
}
int src_width = options.outWidth;
int src_height = options.outHeight;
if( src_width <= 0 || src_height <= 0 ){
Utils.showToast( this, false, "could not get image bounds." );
break;
}
// 長辺
int size = ( src_width > src_height ? src_width : src_height );
// 設定からリサイズ指定を読む
int resize_to = list_resize_max[ pref.getInt( Pref.KEY_RESIZE_IMAGE, 4 ) ];
// リサイズも回転も必要がない場合
if( ( orientation == null || orientation == 1 )
&& ( resize_to <= 0 || size <= resize_to )
){
log.d( "createOpener: no need to resize & rotate" );
break;
}
//noinspection StatementWithEmptyBody
if( size > resize_to ){
// 縮小が必要
}else{
// 縮小は不要
resize_to = size;
}
// inSampleSizeを計算
int bits = 0;
int x = size;
while( x > resize_to * 2 ){
++ bits;
x >>= 1;
}
options.inJustDecodeBounds = false;
options.inSampleSize = 1 << bits;
is = getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( this, false, "could not open image." );
break;
}
Bitmap src;
try{
src = BitmapFactory.decodeStream( is, null, options );
}finally{
IOUtils.closeQuietly( is );
}
if( src == null ){
Utils.showToast( this, false, "could not decode image." );
break;
}
try{
src_width = options.outWidth;
src_height = options.outHeight;
float scale;
int dst_width;
int dst_height;
if( src_width >= src_height ){
scale = resize_to / (float) src_width;
dst_width = resize_to;
dst_height = (int) ( 0.5f + src_height / (float) src_width * resize_to );
if( dst_height < 1 ) dst_height = 1;
}else{
scale = resize_to / (float) src_height;
dst_height = resize_to;
dst_width = (int) ( 0.5f + src_width / (float) src_height * resize_to );
if( dst_width < 1 ) dst_width = 1;
}
Matrix matrix = new Matrix();
matrix.reset();
// 画像の中心が原点に来るようにして
matrix.postTranslate( src_width * - 0.5f, src_height * - 0.5f );
// スケーリング
matrix.postScale( scale, scale );
// 回転情報があれば回転
if( orientation != null ){
int tmp;
switch( orientation.shortValue() ){
default:
break;
case 2:
matrix.postScale( 1f, - 1f );
break; // 上下反転
case 3:
matrix.postRotate( 180f );
break; // 180度回転
case 4:
matrix.postScale( - 1f, 1f );
break; // 左右反転
case 5:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postScale( 1f, - 1f );
matrix.postRotate( - 90f );
break;
case 6:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postRotate( 90f );
break;
case 7:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postScale( 1f, - 1f );
matrix.postRotate( 90f );
break;
case 8:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postRotate( - 90f );
break;
}
}
// 表示領域に埋まるように平行移動
matrix.postTranslate( dst_width * 0.5f, dst_height * 0.5f );
// 出力用Bitmap作成
Bitmap dst = Bitmap.createBitmap( dst_width, dst_height, Bitmap.Config.ARGB_8888 );
if( dst == null ){
Utils.showToast( this, false, "bitmap creation failed." );
break;
}
Bitmap bitmap = Utils.createResizedBitmap( log, this, uri, true,resize_to );
if( bitmap != null ){
try{
Canvas canvas = new Canvas( dst );
Paint paint = new Paint();
paint.setFilterBitmap( true );
canvas.drawBitmap( src, matrix, paint );
File cache_dir = getExternalCacheDir();
if( cache_dir == null ){
Utils.showToast( this, false, "getExternalCacheDir returns null." );
@ -1052,15 +883,15 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
FileOutputStream os = new FileOutputStream( temp_file );
try{
if( is_jpeg ){
dst.compress( Bitmap.CompressFormat.JPEG, 95, os );
bitmap.compress( Bitmap.CompressFormat.JPEG, 95, os );
}else{
dst.compress( Bitmap.CompressFormat.PNG, 100, os );
bitmap.compress( Bitmap.CompressFormat.PNG, 100, os );
}
}finally{
os.close();
}
log.d( "createOpener: resized to %sx%s", dst_width, dst_height );
return new InputStreamOpener() {
return new ActPost.InputStreamOpener() {
@Override public InputStream open() throws IOException{
return new FileInputStream( temp_file );
}
@ -1075,10 +906,8 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
}
};
}finally{
dst.recycle();
bitmap.recycle();
}
}finally{
src.recycle();
}
}catch( Throwable ex ){
@ -1224,7 +1053,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
pa.callback.onPostAttachmentComplete( pa );
}
}.execute();
}.executeOnExecutor( App1.task_executor );
}
// 添付メディア投稿が完了したら呼ばれる
@ -1564,7 +1393,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
}
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor( App1.task_executor );
}
/////////////////////////////////////////////////
@ -1579,7 +1408,7 @@ public class ActPost extends AppCompatActivity implements View.OnClickListener,
}else{
llReply.setVisibility( View.VISIBLE );
tvReplyTo.setText( HTMLDecoder.decodeHTML( account, in_reply_to_text ) );
ivReply.setCornerRadius( pref,16f );
ivReply.setCornerRadius( pref, 16f );
ivReply.setImageUrl( in_reply_to_image, App1.getImageLoader() );
}

View File

@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.support.v4.util.LruCache;
import android.widget.ImageView;
@ -19,7 +20,13 @@ import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import jp.juggler.subwaytooter.table.AcctColor;
import jp.juggler.subwaytooter.table.AcctSet;
@ -212,6 +219,13 @@ public class App1 extends Application {
public static SharedPreferences pref;
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static ThreadPoolExecutor task_executor;
@Override
public void onCreate(){
super.onCreate();
@ -221,6 +235,40 @@ public class App1 extends Application {
.build()
);
if( task_executor == null ){
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
int CPU_COUNT = Runtime.getRuntime().availableProcessors();
int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
int KEEP_ALIVE_SECONDS = 30;
// デフォルトだとキューはmax128で溢れることがある
BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(999);
ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread( @NonNull Runnable r) {
return new Thread(r, "SubwayTooterTask #" + mCount.getAndIncrement());
}
};
task_executor = new ThreadPoolExecutor(
CORE_POOL_SIZE // pool size
, MAXIMUM_POOL_SIZE // max pool size
, KEEP_ALIVE_SECONDS // keep-alive-seconds
, TimeUnit.SECONDS // unit of keep-alive-seconds
, sPoolWorkQueue
, sThreadFactory
);
task_executor.allowCoreThreadTimeOut(true);
}
if( pref == null ){
pref = Pref.pref( getApplicationContext() );
}

View File

@ -5,7 +5,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.os.AsyncTaskCompat;
import android.text.TextUtils;
import android.view.View;
import android.widget.ListView;
@ -94,7 +93,11 @@ class Column {
private static final String KEY_DONT_SHOW_REPLY = "dont_show_reply";
private static final String KEY_REGEX_TEXT = "regex_text";
// static final java.lang.String KEY_COLUMN_COLOR = "color";
static final String KEY_HEADER_BACKGROUND_COLOR = "header_background_color";
static final String KEY_HEADER_TEXT_COLOR = "header_text_color";
static final String KEY_COLUMN_BACKGROUND_COLOR = "column_background_color";
static final String KEY_COLUMN_BACKGROUND_IMAGE = "column_background_image";
static final String KEY_COLUMN_BACKGROUND_IMAGE_ALPHA = "column_background_image_alpha";
private static final String KEY_PROFILE_ID = "profile_id";
private static final String KEY_PROFILE_TAB = "tab";
@ -138,6 +141,12 @@ class Column {
boolean dont_show_reply;
String regex_text;
int header_bg_color;
int header_fg_color;
int column_bg_color;
String column_bg_image;
float column_bg_image_alpha = 1f;
private long profile_id;
volatile TootAccount who_account;
int profile_tab = TAB_STATUS;
@ -193,6 +202,13 @@ class Column {
item.put( KEY_DONT_SHOW_REPLY, dont_show_reply );
item.put( KEY_REGEX_TEXT, regex_text );
item.put( KEY_HEADER_BACKGROUND_COLOR, header_bg_color );
item.put( KEY_HEADER_TEXT_COLOR, header_fg_color );
item.put( KEY_COLUMN_BACKGROUND_COLOR, column_bg_color );
item.put( KEY_COLUMN_BACKGROUND_IMAGE, column_bg_image );
item.put( KEY_COLUMN_BACKGROUND_IMAGE_ALPHA, (double) column_bg_image_alpha );
switch( column_type ){
case TYPE_CONVERSATION:
case TYPE_BOOSTED_BY:
@ -235,6 +251,12 @@ class Column {
this.dont_show_reply = src.optBoolean( KEY_DONT_SHOW_REPLY );
this.regex_text = Utils.optStringX( src, KEY_REGEX_TEXT );
this.header_bg_color = src.optInt( KEY_HEADER_BACKGROUND_COLOR );
this.header_fg_color = src.optInt( KEY_HEADER_TEXT_COLOR );
this.column_bg_color = src.optInt( KEY_COLUMN_BACKGROUND_COLOR );
this.column_bg_image = Utils.optStringX( src, KEY_COLUMN_BACKGROUND_IMAGE );
this.column_bg_image_alpha = (float)src.optDouble( KEY_COLUMN_BACKGROUND_IMAGE_ALPHA ,1.0f);
switch( column_type ){
case TYPE_CONVERSATION:
@ -446,19 +468,14 @@ class Column {
boolean bSimpleList;
boolean bFirstInitialized = false;
private void init(){
bSimpleList = ( column_type != Column.TYPE_CONVERSATION && app_state.pref.getBoolean( Pref.KEY_SIMPLE_LIST, false ) );
startLoading();
}
void onNicknameUpdated(){
fireShowColumnHeader();
}
@ -820,6 +837,7 @@ class Column {
void startLoading(){
cancelLastTask();
bFirstInitialized = true;
list_data.clear();
mRefreshLoadingError = null;
bRefreshLoading = false;
@ -1107,7 +1125,7 @@ class Column {
}
};
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor(App1.task_executor);
}
private static final Pattern reMaxId = Pattern.compile( "&max_id=(\\d+)" ); // より古いデータの取得に使う
@ -1831,7 +1849,7 @@ class Column {
}
};
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor(App1.task_executor);
}
@ -2238,7 +2256,7 @@ class Column {
}
};
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor(App1.task_executor);
}
private static final int heightSpec = View.MeasureSpec.makeMeasureSpec( 0, View.MeasureSpec.UNSPECIFIED );

View File

@ -1,5 +1,9 @@
package jp.juggler.subwaytooter;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.text.Editable;
@ -12,6 +16,7 @@ import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@ -37,12 +42,12 @@ class ColumnViewHolder
final ActMain activity;
final Column column;
final AtomicBoolean is_destroyed = new AtomicBoolean( false );
final ItemListAdapter status_adapter;
private final ItemListAdapter status_adapter;
ColumnViewHolder( ActMain activity, Column column ){
this.activity = activity;
this.column = column;
this.status_adapter = new ItemListAdapter( activity,column );
this.status_adapter = new ItemListAdapter( activity, column );
}
private boolean isPageDestroyed(){
@ -50,11 +55,16 @@ class ColumnViewHolder
}
void onPageDestroy( @SuppressWarnings("UnusedParameters") View root ){
saveScrollPosition();
log.d( "onPageDestroy:%s", column.getColumnName( true ) );
saveScrollPosition();
column.setColumnViewHolder( null );
closeBitmaps();
activity.closeListItemPopup();
}
private TextView tvLoading;
@ -69,24 +79,42 @@ class ColumnViewHolder
private View llColumnSetting;
private EditText etRegexFilter;
private TextView tvRegexFilterError;
private View btnColumnClose;
private ImageView ivColumnIcon;
private View llColumnHeader;
private TextView tvColumnIndex;
private ImageButton btnColumnSetting;
private ImageButton btnColumnReload;
private ImageButton btnColumnClose;
private View flColumnBackground;
private ImageView ivColumnBackgroundImage;
void onPageCreate( View root, int page_idx, int page_count ){
log.d( "onPageCreate:%s", column.getColumnName( true ) );
( (TextView) root.findViewById( R.id.tvColumnIndex ) )
.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
flColumnBackground = root.findViewById( R.id.flColumnBackground );
ivColumnBackgroundImage = (ImageView) root.findViewById( R.id.ivColumnBackgroundImage );
llColumnHeader = root.findViewById( R.id.llColumnHeader );
tvColumnIndex = (TextView) root.findViewById( R.id.tvColumnIndex );
tvColumnIndex.setText( activity.getString( R.string.column_index, page_idx + 1, page_count ) );
tvColumnName = (TextView) root.findViewById( R.id.tvColumnName );
tvColumnContext = (TextView) root.findViewById( R.id.tvColumnContext );
ivColumnIcon = (ImageView) root.findViewById( R.id.ivColumnIcon );
btnColumnClose = root.findViewById( R.id.btnColumnClose );
btnColumnSetting = (ImageButton) root.findViewById( R.id.btnColumnSetting );
btnColumnReload = (ImageButton) root.findViewById( R.id.btnColumnReload );
btnColumnClose = (ImageButton) root.findViewById( R.id.btnColumnClose );
btnColumnSetting.setOnClickListener( this );
btnColumnReload.setOnClickListener( this );
btnColumnClose.setOnClickListener( this );
root.findViewById( R.id.btnColumnReload ).setOnClickListener( this );
root.findViewById( R.id.llColumnHeader ).setOnClickListener( this );
llColumnHeader.setOnClickListener( this );
root.findViewById( R.id.btnColor ).setOnClickListener( this );
tvLoading = (TextView) root.findViewById( R.id.tvLoading );
listView = (MyListView) root.findViewById( R.id.listView );
@ -130,10 +158,7 @@ class ColumnViewHolder
break;
}
View btnColumnSetting = root.findViewById( R.id.btnColumnSetting );
llColumnSetting = root.findViewById( R.id.llColumnSetting );
btnColumnSetting.setVisibility( View.VISIBLE );
btnColumnSetting.setOnClickListener( this );
llColumnSetting.setVisibility( View.GONE );
CheckBox cb;
@ -214,7 +239,7 @@ class ColumnViewHolder
}
if( column.column_type == Column.TYPE_PROFILE ){
vh_header = new HeaderViewHolder( activity,column, listView );
vh_header = new HeaderViewHolder( activity, column, listView );
listView.addHeaderView( vh_header.viewRoot );
}
@ -231,9 +256,103 @@ class ColumnViewHolder
//
column.setColumnViewHolder( this );
showColumnColor();
showContent();
}
void showColumnColor(){
int c = column.header_bg_color;
if( c == 0 ){
llColumnHeader.setBackgroundResource( R.drawable.btn_bg_ddd );
}else{
ViewCompat.setBackground( llColumnHeader,Styler.getAdaptiveRippleDrawable(
c,
(column.header_fg_color != 0 ? column.header_fg_color :
Styler.getAttributeColor( activity,R.attr.colorRippleEffect ))
) );
}
c = column.header_fg_color;
if( c == 0 ){
tvColumnIndex.setTextColor( Styler.getAttributeColor( activity, R.attr.colorColumnHeaderPageNumber ) );
tvColumnName.setTextColor( Styler.getAttributeColor( activity, android.R.attr.textColorPrimary ) );
Styler.setIconDefaultColor( activity, ivColumnIcon, Column.getIconAttrId( column.column_type ) );
Styler.setIconDefaultColor( activity, btnColumnSetting, R.attr.ic_tune );
Styler.setIconDefaultColor( activity, btnColumnReload, R.attr.btn_refresh );
Styler.setIconDefaultColor( activity, btnColumnClose, R.attr.btn_close );
}else{
tvColumnIndex.setTextColor( c );
tvColumnName.setTextColor( c );
Styler.setIconCustomColor( activity, ivColumnIcon, c, Column.getIconAttrId( column.column_type ) );
Styler.setIconCustomColor( activity, btnColumnSetting, c, R.attr.ic_tune );
Styler.setIconCustomColor( activity, btnColumnReload, c, R.attr.btn_refresh );
Styler.setIconCustomColor( activity, btnColumnClose, c, R.attr.btn_close );
}
c = column.column_bg_color;
if( c == 0 ){
ViewCompat.setBackground( flColumnBackground, null );
}else{
flColumnBackground.setBackgroundColor( c );
}
ivColumnBackgroundImage.setAlpha( column.column_bg_image_alpha );
loadBackgroundImage( ivColumnBackgroundImage, column.column_bg_image );
}
private String last_image_uri;
private Bitmap last_image_bitmap;
private void closeBitmaps(){
try{
if( last_image_bitmap != null ){
ivColumnBackgroundImage.setVisibility( View.GONE );
ivColumnBackgroundImage.setImageDrawable( null );
last_image_uri = null;
last_image_bitmap.recycle();
last_image_bitmap = null;
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}
private void loadBackgroundImage( ImageView iv, String url ){
try{
if( TextUtils.isEmpty( url ) ){
closeBitmaps();
return;
}else if( url.equals( last_image_uri ) ){
// 今表示してるのと同じ
return;
}
// 直前のBitmapを掃除する
closeBitmaps();
iv.setVisibility( View.VISIBLE );
int w = iv.getResources().getDisplayMetrics().widthPixels;
int h = iv.getResources().getDisplayMetrics().heightPixels;
int resize_max = ( w > h ? w : h );
Uri uri = Uri.parse( url );
last_image_bitmap = Utils.createResizedBitmap( log, activity, uri, false, resize_max );
if( last_image_bitmap != null ){
iv.setImageBitmap( last_image_bitmap );
last_image_uri = url;
}
}catch( Throwable ex ){
ex.printStackTrace();
}
}
private final Runnable proc_start_filter = new Runnable() {
@Override public void run(){
if( isPageDestroyed() ) return;
@ -274,7 +393,7 @@ class ColumnViewHolder
}
@Override public void onRefresh( SwipyRefreshLayoutDirection direction ){
column.startRefresh( false, direction == SwipyRefreshLayoutDirection.BOTTOM ,-1L,-1);
column.startRefresh( false, direction == SwipyRefreshLayoutDirection.BOTTOM, - 1L, - 1 );
}
@Override public void onCheckedChanged( CompoundButton view, boolean isChecked ){
@ -342,6 +461,11 @@ class ColumnViewHolder
case R.id.btnDeleteNotification:
activity.deleteNotification( false, column.access_info );
break;
case R.id.btnColor:
int idx = activity.app_state.column_list.indexOf( column );
ActColumnCustomize.open( activity, idx, ActMain.REQUEST_COLUMN_COLOR );
break;
}
}
@ -403,8 +527,6 @@ class ColumnViewHolder
showColumnCloseButton();
ivColumnIcon.setImageResource( Styler.getAttributeResourceId( activity, Column.getIconAttrId( column.column_type ) ) );
}
void showContent(){
@ -420,6 +542,11 @@ class ColumnViewHolder
vh_header.bind( column.who_account );
}
if( ! column.bFirstInitialized ){
showError( "initializing" );
return;
}
if( column.bInitialLoading ){
String message = column.task_progress;
if( message == null ) message = "loading?";
@ -466,7 +593,7 @@ class ColumnViewHolder
if( listView.getVisibility() == View.VISIBLE ){
column.scroll_save = new ScrollPosition( listView );
}else{
column.scroll_save = new ScrollPosition( 0,0 );
column.scroll_save = new ScrollPosition( 0, 0 );
}
}

View File

@ -1,109 +0,0 @@
//package jp.juggler.subwaytooter;
//
//import android.os.AsyncTask;
//import android.os.SystemClock;
//import android.support.annotation.NonNull;
//
//import java.util.HashMap;
//
//import jp.juggler.subwaytooter.api.TootApiClient;
//import jp.juggler.subwaytooter.api.TootApiResult;
//import jp.juggler.subwaytooter.api.entity.TootRelationShip;
//import jp.juggler.subwaytooter.table.SavedAccount;
//import jp.juggler.subwaytooter.util.LogCategory;
//
//class RelationshipMap {
//
// private static final LogCategory log = new LogCategory( "RelationshipMap" );
//
//
// interface UpdateCallback {
// void onRelationShipUpdate();
// }
//
// private static class RelationshipPerAccount extends HashMap< Long, TootRelationShip > {
// long last_update;
// }
//
// private final HashMap< String, RelationshipPerAccount > map_account = new HashMap<>();
//
// @NonNull
// private RelationshipPerAccount getRelationshipPerAccount( @NonNull SavedAccount access_info ){
// RelationshipPerAccount ra;
// ra = map_account.get( access_info.acct );
// if( ra == null ){
// ra = new RelationshipPerAccount();
// map_account.put( access_info.acct, ra );
// }
// return ra;
// }
//
// public TootRelationShip get( @NonNull SavedAccount access_info, long id ){
// return getRelationshipPerAccount( access_info ).get( id );
// }
//
// public void put( @NonNull SavedAccount access_info, @NonNull TootRelationShip relation ){
// getRelationshipPerAccount( access_info ).put( relation.id,relation );
// }
//
// public void addFollowing( SavedAccount access_info, long id ){
// RelationshipPerAccount ra = getRelationshipPerAccount( access_info );
// TootRelationShip rs = ra.get( id);
// if(rs == null ){
// rs = new TootRelationShip();
// ra.put( id, rs );
// }
// rs.following = true;
// }
//
// void checkUpdate( @NonNull final ActMain activity, @NonNull final SavedAccount access_info, final UpdateCallback callback ){
// final RelationshipPerAccount ra = getRelationshipPerAccount( access_info );
// long now = SystemClock.elapsedRealtime();
// if( now - ra.last_update < 300000L ) return;
// ra.last_update = now;
//
// new AsyncTask< Void, Void, TootApiResult >() {
//
// TootRelationShip.List list;
//
// @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 ){
// }
// } );
//
// client.setAccount( access_info );
// TootApiResult result = client.request( "/api/v1/accounts/relationships" );
// if( result != null && result.array != null ){
// list = TootRelationShip.parseList( log, result.array );
// }
// return result;
// }
//
// @Override
// protected void onCancelled( TootApiResult result ){
// onPostExecute( null );
// }
//
// @Override
// protected void onPostExecute( TootApiResult result ){
//
// if( isCancelled() || result == null ){
// return;
// }
// if( list != null ){
// for( TootRelationShip item : list ){
// ra.put( item.id, item );
// }
// callback.onRelationShipUpdate();
// }
// }
// }.execute();
// }
//}

View File

@ -1,16 +1,25 @@
package jp.juggler.subwaytooter;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.RoundRectShape;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import java.util.Arrays;
import java.util.Locale;
import jp.juggler.subwaytooter.api.entity.TootStatus;
@ -120,4 +129,50 @@ public class Styler {
}
static void setIconDefaultColor( Context context,ImageView iv, int icon_attr ){
iv.setImageResource( Styler.getAttributeResourceId( context, icon_attr ) );
}
static void setIconCustomColor( Context context,ImageView iv, int color,int icon_attr ){
Drawable d = Styler.getAttributeDrawable( context,icon_attr ).mutate();
d.setColorFilter( color, PorterDuff.Mode.SRC_ATOP );
iv.setImageDrawable( d );
}
static Drawable getAdaptiveRippleDrawable( int normalColor, int pressedColor){
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return new RippleDrawable(
ColorStateList.valueOf(pressedColor)
,getShape(normalColor)
, null
);
} else {
return getStateListDrawable(normalColor, pressedColor);
}
}
private static Drawable getShape(int color) {
RectShape r = new RectShape();
ShapeDrawable shapeDrawable = new ShapeDrawable(r);
shapeDrawable.getPaint().setColor(color);
return shapeDrawable;
}
private static StateListDrawable getStateListDrawable( int normalColor, int pressedColor) {
StateListDrawable states = new StateListDrawable();
states.addState(new int[]{android.R.attr.state_pressed},
new ColorDrawable(pressedColor));
states.addState(new int[]{android.R.attr.state_focused},
new ColorDrawable(pressedColor));
states.addState(new int[]{android.R.attr.state_activated},
new ColorDrawable(pressedColor));
states.addState(new int[]{},
new ColorDrawable(normalColor));
return states;
}
}

View File

@ -13,6 +13,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import jp.juggler.subwaytooter.ActMain;
import jp.juggler.subwaytooter.App1;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.util.Utils;
@ -59,7 +60,7 @@ public class DlgQRCode {
} );
progress.show();
AsyncTaskCompat.executeParallel( task );
task.executeOnExecutor( App1.task_executor);
}
public static void open( @NonNull final ActMain activity ,final CharSequence message,final String url ){

View File

@ -2,12 +2,15 @@ package jp.juggler.subwaytooter.util;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -18,6 +21,11 @@ import java.util.Locale;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@ -38,6 +46,7 @@ import android.view.inputmethod.InputMethodManager;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.w3c.dom.Element;
@ -50,7 +59,9 @@ import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import it.sephiroth.android.library.exif2.ExifInterface;
import jp.juggler.subwaytooter.ActMain;
import jp.juggler.subwaytooter.ActPost;
public class Utils {
@ -87,49 +98,49 @@ public class Utils {
public static String formatBytes( long t ){
return bytes_format.format( t );
// StringBuilder sb = new StringBuilder();
// long n;
// // giga
// n = t / 1000000000L;
// if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dg", n ) );
// t -= n * 1000000000L;
// }
// // Mega
// n = t / 1000000L;
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03dm", n ) );
// t -= n * 1000000L;
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dm", n ) );
// t -= n * 1000000L;
// }
// // kilo
// n = t / 1000L;
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03dk", n ) );
// t -= n * 1000L;
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dk", n ) );
// t -= n * 1000L;
// }
// // remain
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03d", t ) );
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%d", t ) );
// }
//
// return sb.toString();
// StringBuilder sb = new StringBuilder();
// long n;
// // giga
// n = t / 1000000000L;
// if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dg", n ) );
// t -= n * 1000000000L;
// }
// // Mega
// n = t / 1000000L;
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03dm", n ) );
// t -= n * 1000000L;
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dm", n ) );
// t -= n * 1000000L;
// }
// // kilo
// n = t / 1000L;
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03dk", n ) );
// t -= n * 1000L;
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%dk", n ) );
// t -= n * 1000L;
// }
// // remain
// if( sb.length() > 0 ){
// sb.append( String.format( Locale.JAPAN, "%03d", t ) );
// }else if( n > 0 ){
// sb.append( String.format( Locale.JAPAN, "%d", t ) );
// }
//
// return sb.toString();
}
// public static PendingIntent createAlarmPendingIntent( Context context ){
// Intent i = new Intent( context.getApplicationContext(), Receiver1.class );
// i.setAction( Receiver1.ACTION_ALARM );
// return PendingIntent.getBroadcast( context.getApplicationContext(), 0, i, 0 );
// }
//
// Intent i = new Intent( context.getApplicationContext(), Receiver1.class );
// i.setAction( Receiver1.ACTION_ALARM );
// return PendingIntent.getBroadcast( context.getApplicationContext(), 0, i, 0 );
// }
//
// 文字列とバイト列の変換
@NonNull public static byte[] encodeUTF8( @NonNull String str ){
try{
@ -164,7 +175,7 @@ public class Utils {
public static String optStringX( JSONArray src, int key ){
return src.isNull( key ) ? null : src.optString( key );
}
public static ArrayList< String > parseStringArray( LogCategory log, JSONArray array ){
ArrayList< String > dst_list = new ArrayList<>();
if( array != null ){
@ -279,15 +290,15 @@ public class Utils {
if( url == null ) return null;
return encodeBase64Safe( encodeSHA256( encodeUTF8( url ) ) );
}
// public static String name2url(String entry) {
// if(entry==null) return null;
// byte[] b = new byte[entry.length()/2];
// for(int i=0,ie=b.length;i<ie;++i){
// b[i]= (byte)((hex2int(entry.charAt(i*2))<<4)| hex2int(entry.charAt(i*2+1)));
// }
// return decodeUTF8(b);
// }
// public static String name2url(String entry) {
// if(entry==null) return null;
// byte[] b = new byte[entry.length()/2];
// for(int i=0,ie=b.length;i<ie;++i){
// b[i]= (byte)((hex2int(entry.charAt(i*2))<<4)| hex2int(entry.charAt(i*2+1)));
// }
// return decodeUTF8(b);
// }
///////////////////////////////////////////////////
@ -424,7 +435,7 @@ public class Utils {
}
}
public static void hideKeyboard(Context context,View v ){
public static void hideKeyboard( Context context, View v ){
InputMethodManager imm = (InputMethodManager) context.getSystemService( Context.INPUT_METHOD_SERVICE );
imm.hideSoftInputFromWindow( v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS );
}
@ -432,99 +443,99 @@ public class Utils {
public static String ellipsize( String t, int max ){
return ( t.length() > max ? t.substring( 0, max - 1 ) + "" : t );
}
// public static int getEnumStringId( String residPrefix, String name,Context context ) {
// name = residPrefix + name;
// try{
// int iv = context.getResources().getIdentifier(name,"string",context.getPackageName() );
// if( iv != 0 ) return iv;
// }catch(Throwable ex){
// }
// log.e("missing resid for %s",name);
// return R.string.Dialog_Cancel;
// }
// public static String getConnectionResultErrorMessage( ConnectionResult connectionResult ){
// int code = connectionResult.getErrorCode();
// String msg = connectionResult.getErrorMessage();
// if( TextUtils.isEmpty( msg ) ){
// switch( code ){
// case ConnectionResult.SUCCESS:
// msg = "SUCCESS";
// break;
// case ConnectionResult.SERVICE_MISSING:
// msg = "SERVICE_MISSING";
// break;
// case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
// msg = "SERVICE_VERSION_UPDATE_REQUIRED";
// break;
// case ConnectionResult.SERVICE_DISABLED:
// msg = "SERVICE_DISABLED";
// break;
// case ConnectionResult.SIGN_IN_REQUIRED:
// msg = "SIGN_IN_REQUIRED";
// break;
// case ConnectionResult.INVALID_ACCOUNT:
// msg = "INVALID_ACCOUNT";
// break;
// case ConnectionResult.RESOLUTION_REQUIRED:
// msg = "RESOLUTION_REQUIRED";
// break;
// case ConnectionResult.NETWORK_ERROR:
// msg = "NETWORK_ERROR";
// break;
// case ConnectionResult.INTERNAL_ERROR:
// msg = "INTERNAL_ERROR";
// break;
// case ConnectionResult.SERVICE_INVALID:
// msg = "SERVICE_INVALID";
// break;
// case ConnectionResult.DEVELOPER_ERROR:
// msg = "DEVELOPER_ERROR";
// break;
// case ConnectionResult.LICENSE_CHECK_FAILED:
// msg = "LICENSE_CHECK_FAILED";
// break;
// case ConnectionResult.CANCELED:
// msg = "CANCELED";
// break;
// case ConnectionResult.TIMEOUT:
// msg = "TIMEOUT";
// break;
// case ConnectionResult.INTERRUPTED:
// msg = "INTERRUPTED";
// break;
// case ConnectionResult.API_UNAVAILABLE:
// msg = "API_UNAVAILABLE";
// break;
// case ConnectionResult.SIGN_IN_FAILED:
// msg = "SIGN_IN_FAILED";
// break;
// case ConnectionResult.SERVICE_UPDATING:
// msg = "SERVICE_UPDATING";
// break;
// case ConnectionResult.SERVICE_MISSING_PERMISSION:
// msg = "SERVICE_MISSING_PERMISSION";
// break;
// case ConnectionResult.RESTRICTED_PROFILE:
// msg = "RESTRICTED_PROFILE";
// break;
//
// }
// }
// return msg;
// }
// public static String getConnectionSuspendedMessage( int i ){
// switch( i ){
// default:
// return "?";
// case GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST:
// return "NETWORK_LOST";
// case GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED:
// return "SERVICE_DISCONNECTED";
// }
// }
// public static int getEnumStringId( String residPrefix, String name,Context context ) {
// name = residPrefix + name;
// try{
// int iv = context.getResources().getIdentifier(name,"string",context.getPackageName() );
// if( iv != 0 ) return iv;
// }catch(Throwable ex){
// }
// log.e("missing resid for %s",name);
// return R.string.Dialog_Cancel;
// }
// public static String getConnectionResultErrorMessage( ConnectionResult connectionResult ){
// int code = connectionResult.getErrorCode();
// String msg = connectionResult.getErrorMessage();
// if( TextUtils.isEmpty( msg ) ){
// switch( code ){
// case ConnectionResult.SUCCESS:
// msg = "SUCCESS";
// break;
// case ConnectionResult.SERVICE_MISSING:
// msg = "SERVICE_MISSING";
// break;
// case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
// msg = "SERVICE_VERSION_UPDATE_REQUIRED";
// break;
// case ConnectionResult.SERVICE_DISABLED:
// msg = "SERVICE_DISABLED";
// break;
// case ConnectionResult.SIGN_IN_REQUIRED:
// msg = "SIGN_IN_REQUIRED";
// break;
// case ConnectionResult.INVALID_ACCOUNT:
// msg = "INVALID_ACCOUNT";
// break;
// case ConnectionResult.RESOLUTION_REQUIRED:
// msg = "RESOLUTION_REQUIRED";
// break;
// case ConnectionResult.NETWORK_ERROR:
// msg = "NETWORK_ERROR";
// break;
// case ConnectionResult.INTERNAL_ERROR:
// msg = "INTERNAL_ERROR";
// break;
// case ConnectionResult.SERVICE_INVALID:
// msg = "SERVICE_INVALID";
// break;
// case ConnectionResult.DEVELOPER_ERROR:
// msg = "DEVELOPER_ERROR";
// break;
// case ConnectionResult.LICENSE_CHECK_FAILED:
// msg = "LICENSE_CHECK_FAILED";
// break;
// case ConnectionResult.CANCELED:
// msg = "CANCELED";
// break;
// case ConnectionResult.TIMEOUT:
// msg = "TIMEOUT";
// break;
// case ConnectionResult.INTERRUPTED:
// msg = "INTERRUPTED";
// break;
// case ConnectionResult.API_UNAVAILABLE:
// msg = "API_UNAVAILABLE";
// break;
// case ConnectionResult.SIGN_IN_FAILED:
// msg = "SIGN_IN_FAILED";
// break;
// case ConnectionResult.SERVICE_UPDATING:
// msg = "SERVICE_UPDATING";
// break;
// case ConnectionResult.SERVICE_MISSING_PERMISSION:
// msg = "SERVICE_MISSING_PERMISSION";
// break;
// case ConnectionResult.RESTRICTED_PROFILE:
// msg = "RESTRICTED_PROFILE";
// break;
//
// }
// }
// return msg;
// }
// public static String getConnectionSuspendedMessage( int i ){
// switch( i ){
// default:
// return "?";
// case GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST:
// return "NETWORK_LOST";
// case GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED:
// return "SERVICE_DISCONNECTED";
// }
// }
static HashMap< String, String > mime_type_ex = null;
static final Object mime_type_ex_lock = new Object();
@ -580,6 +591,193 @@ public class Utils {
return sb;
}
public static Bitmap createResizedBitmap( LogCategory log, Context context, Uri uri, boolean skipIfNoNeedToResizeAndRotate, int resize_to ){
try{
// EXIF回転情報の取得
Integer orientation;
InputStream is = context.getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( context, false, "could not open image." );
return null;
}
try{
ExifInterface exif = new ExifInterface();
exif.readExif( is, ExifInterface.Options.OPTION_IFD_0 | ExifInterface.Options.OPTION_IFD_1 | ExifInterface.Options.OPTION_IFD_EXIF );
orientation = exif.getTagIntValue( ExifInterface.TAG_ORIENTATION );
}finally{
IOUtils.closeQuietly( is );
}
// 画像のサイズを調べる
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inScaled = false;
is = context.getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( context, false, "could not open image." );
return null;
}
try{
BitmapFactory.decodeStream( is, null, options );
}finally{
IOUtils.closeQuietly( is );
}
int src_width = options.outWidth;
int src_height = options.outHeight;
if( src_width <= 0 || src_height <= 0 ){
Utils.showToast( context, false, "could not get image bounds." );
return null;
}
// 長辺
int size = ( src_width > src_height ? src_width : src_height );
// リサイズも回転も必要がない場合
if( skipIfNoNeedToResizeAndRotate
&& ( orientation == null || orientation == 1 )
&& ( resize_to <= 0 || size <= resize_to )
){
log.d( "createOpener: no need to resize & rotate" );
return null;
}
//noinspection StatementWithEmptyBody
if( size > resize_to ){
// 縮小が必要
}else{
// 縮小は不要
resize_to = size;
}
// inSampleSizeを計算
int bits = 0;
int x = size;
while( x > resize_to * 2 ){
++ bits;
x >>= 1;
}
options.inJustDecodeBounds = false;
options.inSampleSize = 1 << bits;
is = context.getContentResolver().openInputStream( uri );
if( is == null ){
Utils.showToast( context, false, "could not open image." );
return null;
}
Bitmap src;
try{
src = BitmapFactory.decodeStream( is, null, options );
}finally{
IOUtils.closeQuietly( is );
}
if( src == null ){
Utils.showToast( context, false, "could not decode image." );
return null;
}
try{
src_width = options.outWidth;
src_height = options.outHeight;
float scale;
int dst_width;
int dst_height;
if( src_width >= src_height ){
scale = resize_to / (float) src_width;
dst_width = resize_to;
dst_height = (int) ( 0.5f + src_height / (float) src_width * resize_to );
if( dst_height < 1 ) dst_height = 1;
}else{
scale = resize_to / (float) src_height;
dst_height = resize_to;
dst_width = (int) ( 0.5f + src_width / (float) src_height * resize_to );
if( dst_width < 1 ) dst_width = 1;
}
Matrix matrix = new Matrix();
matrix.reset();
// 画像の中心が原点に来るようにして
matrix.postTranslate( src_width * - 0.5f, src_height * - 0.5f );
// スケーリング
matrix.postScale( scale, scale );
// 回転情報があれば回転
if( orientation != null ){
int tmp;
switch( orientation.shortValue() ){
default:
break;
case 2:
matrix.postScale( 1f, - 1f );
break; // 上下反転
case 3:
matrix.postRotate( 180f );
break; // 180度回転
case 4:
matrix.postScale( - 1f, 1f );
break; // 左右反転
case 5:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postScale( 1f, - 1f );
matrix.postRotate( - 90f );
break;
case 6:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postRotate( 90f );
break;
case 7:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postScale( 1f, - 1f );
matrix.postRotate( 90f );
break;
case 8:
tmp = dst_width;
//noinspection SuspiciousNameCombination
dst_width = dst_height;
dst_height = tmp;
matrix.postRotate( - 90f );
break;
}
}
// 表示領域に埋まるように平行移動
matrix.postTranslate( dst_width * 0.5f, dst_height * 0.5f );
// 出力用Bitmap作成
Bitmap dst = Bitmap.createBitmap( dst_width, dst_height, Bitmap.Config.ARGB_8888 );
if( dst == null ){
Utils.showToast( context, false, "bitmap creation failed." );
return null;
}
try{
Canvas canvas = new Canvas( dst );
Paint paint = new Paint();
paint.setFilterBitmap( true );
canvas.drawBitmap( src, matrix, paint );
log.d( "createResizedBitmap: resized to %sx%s", dst_width, dst_height );
Bitmap tmp = dst;
dst = null;
return tmp;
}finally{
if( dst != null ) dst.recycle();
}
}finally{
src.recycle();
}
}catch( Throwable ex ){
ex.printStackTrace();
}
return null;
}
static class FileInfo {
Uri uri;
@ -610,13 +808,13 @@ public class Utils {
StorageManager sm = (StorageManager) context.getApplicationContext().getSystemService( Context.STORAGE_SERVICE );
// SDカードスロットのある7.0端末が手元にないから検証できない
// if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){
// for(StorageVolume volume : sm.getStorageVolumes() ){
// // String path = volume.getPath();
// String state = volume.getState();
//
// }
// }
// if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){
// for(StorageVolume volume : sm.getStorageVolumes() ){
// // String path = volume.getPath();
// String state = volume.getState();
//
// }
// }
Method getVolumeList = sm.getClass().getMethod( "getVolumeList" );
Object[] volumes = (Object[]) getVolumeList.invoke( sm );

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbarStyle="outsideOverlay"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp"
>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:text="@string/column_header"
/>
<LinearLayout style="@style/setting_row_form">
<LinearLayout
android:id="@+id/llColumnHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="3dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:paddingTop="3dp"
android:layout_marginBottom="6dp"
>
<ImageView
android:id="@+id/ivColumnHeader"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="4dp"
android:importantForAccessibility="no"
tools:src="?attr/btn_federate_tl"
/>
<TextView
android:id="@+id/tvColumnName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:text="@string/federate_timeline"
/>
</LinearLayout>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/background_color"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnHeaderBackgroundEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
/>
<Button
android:id="@+id/btnHeaderBackgroundReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/foreground_color"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnHeaderTextEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
android:textAllCaps="false"
/>
<Button
android:id="@+id/btnHeaderTextReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:text="@string/column_background"
/>
<LinearLayout style="@style/setting_row_form">
<FrameLayout
android:id="@+id/flColumnBackground"
android:layout_width="match_parent"
android:layout_height="48dp"
>
<ImageView
android:id="@+id/ivColumnBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
/>
</FrameLayout>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/color"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnColumnBackgroundColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
android:textAllCaps="false"
/>
<Button
android:id="@+id/btnColumnBackgroundColorReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/image"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<Button
android:id="@+id/btnColumnBackgroundImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pick_image"
android:textAllCaps="false"
/>
<Button
android:id="@+id/btnColumnBackgroundImageReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<TextView
style="@style/setting_row_label"
android:text="@string/image_alpha"
/>
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<SeekBar
android:id="@+id/sbColumnBackgroundAlpha"
style="@style/setting_horizontal_stretch"
android:layout_height="48dp"
android:paddingEnd="32dp"
android:paddingStart="32dp"
/>
</LinearLayout>
<View style="@style/setting_divider"/>
</LinearLayout>
</ScrollView>

View File

@ -12,62 +12,120 @@
tools:openDrawer="start"
>
<FrameLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/llEmpty"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
<LinearLayout
android:id="@+id/llEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/column_empty"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/column_empty"
/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
>
<ImageButton
android:id="@+id/btnMenu"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
app:srcCompat="?attr/ic_hamburger"
android:contentDescription="@string/menu"
/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/colorImageButton"
/>
<HorizontalScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:cacheColorHint="#00000000"
android:fadingEdge="horizontal"
android:fadingEdgeLength="20dp"
android:fillViewport="true"
android:scrollbars="none"
android:background="?attr/colorColumnStripBackground"
android:id="@+id/svColumnStrip"
>
<LinearLayout
android:id="@+id/llColumnStrip"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
/>
</HorizontalScrollView>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="?attr/colorImageButton"
/>
<ImageButton
android:id="@+id/btnToot"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/btn_bg_ddd"
app:srcCompat="?attr/ic_edit"
android:contentDescription="@string/toot"
/>
</LinearLayout>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabToot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
app:srcCompat="?attr/ic_toot"
/>
<!--<android.support.design.widget.FloatingActionButton-->
<!--android:id="@+id/"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_gravity="bottom|end"-->
<!--android:layout_marginBottom="4dp"-->
<!--android:layout_marginEnd="4dp"-->
<!--app:srcCompat="?attr/ic_toot"-->
<!--/>-->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_marginStart="4dp"
android:layout_marginBottom="4dp"
app:srcCompat="?attr/ic_menu"
/>
<!--<ImageButton-->
<!--android:id="@+id/fabMenu"-->
<!--android:layout_width="48dp"-->
<!--android:layout_height="48dp"-->
<!--android:layout_gravity="top|start"-->
<!--android:background="@drawable/btn_bg_transparent"-->
<!--app:srcCompat="?attr/ic_hamburger"-->
<!--/>-->
</FrameLayout>
<!--<android.support.design.widget.FloatingActionButton-->
<!--android:id="@+id/fabMenu"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_gravity="bottom|start"-->
<!--android:layout_marginBottom="4dp"-->
<!--android:layout_marginStart="4dp"-->
<!--app:srcCompat="?attr/ic_menu"-->
<!--/>-->
<!--<android.support.design.widget.CoordinatorLayout-->

View File

@ -66,6 +66,7 @@
<TextView
style="@style/setting_row_label"
android:text="@string/nickname_label"
android:labelFor="@+id/etNickname"
/>
<LinearLayout style="@style/setting_row_form">
@ -94,6 +95,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
android:textAllCaps="false"
/>
<Button
@ -101,6 +103,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
@ -119,6 +122,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
android:textAllCaps="false"
/>
<Button
@ -126,6 +130,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reset"
android:textAllCaps="false"
/>
</LinearLayout>
@ -157,6 +162,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/save"
android:textAllCaps="false"
/>
<Button
@ -166,6 +172,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/discard"
android:textAllCaps="false"
/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@drawable/btn_bg_ddd"
android:gravity="center"
android:orientation="vertical"
>
<ImageView
android:id="@+id/ivIcon"
android:layout_marginTop="4dp"
android:layout_width="32dp"
android:layout_height="32dp"
tools:ignore="ContentDescription"
android:importantForAccessibility="no"
/>
<View
android:id="@+id/vAcctColor"
android:layout_width="32dp"
android:layout_height="3dp"
android:layout_marginTop="3dp"
/>
</LinearLayout>

View File

@ -64,6 +64,7 @@
android:layout_height="32dp"
android:layout_marginEnd="4dp"
android:id="@+id/ivColumnIcon"
android:importantForAccessibility="no"
/>
<TextView
android:id="@+id/tvColumnName"
@ -192,6 +193,15 @@
android:id="@+id/btnDeleteNotification"
android:textAllCaps="false"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/color_and_background"
android:id="@+id/btnColor"
android:textAllCaps="false"
/>
</LinearLayout>
<RelativeLayout
@ -235,38 +245,49 @@
</RelativeLayout>
<TextView
android:id="@+id/tvLoading"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
/>
android:id="@+id/flColumnBackground"
>
<com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout
android:id="@+id/swipyRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srl_direction="both">
<jp.juggler.subwaytooter.util.MyListView
android:id="@+id/listView"
<ImageView
android:id="@+id/ivColumnBackgroundImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false"
android:fadeScrollbars="false"
android:fastScrollEnabled="true"
android:paddingBottom="64dp"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
android:dividerHeight="1dp"
android:divider="?attr/colorSettingDivider"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="gone"
/>
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>
<TextView
android:id="@+id/tvLoading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
/>
<com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout
android:id="@+id/swipyRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srl_direction="both">
<jp.juggler.subwaytooter.util.MyListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fadeScrollbars="false"
android:fastScrollEnabled="true"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:scrollbarStyle="outsideOverlay"
android:dividerHeight="1dp"
android:divider="?attr/colorSettingDivider"
/>
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>
</FrameLayout>
</LinearLayout>

View File

@ -277,5 +277,14 @@
<string name="toot">Toot</string>
<string name="visibility">Visibility</string>
<string name="dont_round_avatar">Don\'t round corner of avatar image (app restart required)</string>
<string name="color_and_background">Color and Background…</string>
<string name="column_background">column background</string>
<string name="column_header">column header</string>
<string name="foreground_color">foreground color</string>
<string name="image">image</string>
<string name="image_alpha">image alpha</string>
<string name="menu">Menu</string>
<string name="open_in_pseudo_account">open in pseudo account %1$s</string>
<string name="pick_image">pick image</string>
</resources>

View File

@ -273,4 +273,13 @@
<string name="toot">トゥート</string>
<string name="visibility">公開範囲</string>
<string name="dont_round_avatar">ユーザ画像を角丸にしない(アプリ再起動が必要)</string>
<string name="color_and_background">色と背景</string>
<string name="column_background">カラム背景</string>
<string name="column_header">カラムヘッダ</string>
<string name="foreground_color">文字とアイコンの色</string>
<string name="image">画像</string>
<string name="image_alpha">画像の不透明度</string>
<string name="menu">メニュー</string>
<string name="open_in_pseudo_account">疑似アカウント %1$s で開く</string>
<string name="pick_image">画像の選択</string>
</resources>

View File

@ -70,6 +70,8 @@
<!-- 投稿画面のEditTextの下に引くFrameLayoutの背景 -->
<attr name="colorPostFormBackground" format="color"/>
<!-- カラムストリップの背景色 -->
<attr name="colorColumnStripBackground" format="color"/>
<attr name="btn_attachment" format="reference" />
<attr name="btn_boost" format="reference" />

View File

@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="Light_colorColumnListItemText">#80000000</color>
<color name="Light_colorTimeSmall">#80000000</color>
<color name="Light_colorAcctSmall">#80000000</color>
<color name="Light_colorColumnHeaderAcct">#80000000</color>
<color name="Light_colorColumnHeaderPageNumber">#80000000</color>
<color name="Light_colorSettingDivider">#80000000</color>
<color name="Light_colorImageButton">#99343434</color>
<color name="Light_colorAccent">#5a5a5a</color>
<color name="Light_colorRippleEffect">#FF808080</color>
<color name="Light_colorColumnListItemText">#808080</color>
<color name="Light_colorTimeSmall">#808080</color>
<color name="Light_colorAcctSmall">#808080</color>
<color name="Light_colorColumnHeaderAcct">#808080</color>
<color name="Light_colorColumnHeaderPageNumber">#808080</color>
<color name="Light_colorSettingDivider">#808080</color>
<color name="Light_colorColumnListDragHandleBackground">#80cccccc</color>
<color name="Light_list_item_bg_pressed_dragged">#AACCCCCC</color>
@ -27,8 +28,11 @@
<color name="Light_colorPostFormBackground">#eee</color>
<color name="Light_colorColumnStripBackground">#FFFFFFFF</color>
<color name="Light_colorProfileBackgroundMask">#C0FFFFFF</color>
<color name="Light_colorPrimaryDark">#303030</color>
<!-- 以下は白テーマでも黒テーマでもだいたい同じ -->
@ -44,18 +48,20 @@
<!-- Dark theme -->
<color name="Dark_colorColumnListItemText">#66FFFFFF</color>
<color name="Dark_colorTimeSmall">#66FFFFFF</color>
<color name="Dark_colorAcctSmall">#66FFFFFF</color>
<color name="Dark_colorColumnHeaderAcct">#66FFFFFF</color>
<color name="Dark_colorColumnHeaderPageNumber">#66FFFFFF</color>
<color name="Dark_colorSettingDivider">#66FFFFFF</color><!-- ダイアログ背景が#424242なので、それより明るくないといけない -->
<color name="Dark_colorImageButton">#ccFFFFFF</color>
<color name="Dark_colorAccent">#ccFFFFFF</color>
<color name="Dark_colorRippleEffect">#FF666666</color>
<color name="Dark_colorColumnListItemText">#666</color>
<color name="Dark_colorTimeSmall">#666</color>
<color name="Dark_colorAcctSmall">#666</color>
<color name="Dark_colorColumnHeaderAcct">#666</color>
<color name="Dark_colorColumnHeaderPageNumber">#666</color>
<color name="Dark_colorSettingDivider">#666</color><!-- ダイアログ背景が#424242なので、それより明るくないといけない -->
<color name="Dark_colorColumnListDragHandleBackground">#80444444</color>
<color name="Dark_list_item_bg_pressed_dragged">#AA444444</color>
@ -72,6 +78,8 @@
<color name="Dark_colorProfileBackgroundMask">#C0000000</color>
<color name="Dark_colorBackground">#000</color>
<color name="Dark_colorColumnStripBackground">#000</color>
<color name="Dark_colorPrimaryDark">#222</color>
<!-- 以下は白テーマでも黒テーマでもだいたい同じ -->

View File

@ -272,4 +272,14 @@
<string name="clear_text">Cleat text</string>
<string name="clear_text_and_media">Clear text and media</string>
<string name="dont_round_avatar">Don\'t round corner of avatar image (app restart required)</string>
<string name="menu">Menu</string>
<string name="open_in_pseudo_account">open in pseudo account %1$s</string>
<string name="color_and_background">Color and Background…</string>
<string name="column_background">column background</string>
<string name="pick_image">pick image</string>
<string name="image_alpha">image alpha</string>
<string name="image">image</string>
<string name="column_header">column header</string>
<string name="foreground_color">foreground color</string>
</resources>

View File

@ -34,6 +34,7 @@
<item name="colorRegexFilterError">@color/Light_colorRegexFilterError</item>
<item name="colorPostFormBackground">@color/Light_colorPostFormBackground</item>
<item name="colorColumnStripBackground">@color/Light_colorColumnStripBackground</item>
<item name="btn_attachment">@drawable/btn_attachment</item>
@ -123,6 +124,7 @@
<item name="colorSettingDivider">@color/Dark_colorSettingDivider</item>
<item name="colorRegexFilterError">@color/Dark_colorRegexFilterError</item>
<item name="colorPostFormBackground">@color/Dark_colorPostFormBackground</item>
<item name="colorColumnStripBackground">@color/Dark_colorColumnStripBackground</item>
<item name="btn_attachment">@drawable/btn_attachment_dark</item>
<item name="btn_boost">@drawable/btn_boost_dark</item>