メディアビューアのロード進捗表示の改善。クラッシュレポート対応
This commit is contained in:
parent
93f40e08a5
commit
2d559a84c0
|
@ -1,16 +1,13 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintButtonStyle" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="AndroidLintSetTextI18n" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="EmptyStatementBody" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="m_reportEmptyBlocks" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
|
||||
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NumericOverflow" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintButtonStyle" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="AndroidLintSetTextI18n" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="EmptyStatementBody" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="m_reportEmptyBlocks" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FieldCanBeLocal" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NumericOverflow" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
|
@ -4,12 +4,16 @@ import android.app.Activity;
|
|||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount;
|
||||
import jp.juggler.subwaytooter.util.Utils;
|
||||
|
||||
/*
|
||||
AsyncTask customized version:
|
||||
|
@ -18,47 +22,83 @@ import jp.juggler.subwaytooter.util.Utils;
|
|||
- has TootApiClient.
|
||||
- pass progress message from TootApiClient to ProgressDialog.
|
||||
*/
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public abstract class TootApiTask extends AsyncTask< Void, Void, TootApiResult > implements TootApiClient.Callback {
|
||||
|
||||
@NonNull protected final TootApiClient client;
|
||||
|
||||
@SuppressWarnings("WeakerAccess") public static final int PROGRESS_NONE = - 1;
|
||||
@SuppressWarnings("WeakerAccess") public static final int PROGRESS_SPINNER = ProgressDialog.STYLE_SPINNER;
|
||||
@SuppressWarnings("WeakerAccess") public static final int PROGRESS_HORIZONTAL = ProgressDialog.STYLE_HORIZONTAL;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, SavedAccount access_info, boolean bShowProgress ){
|
||||
this( _activity, access_info, bShowProgress ? PROGRESS_SPINNER : PROGRESS_NONE );
|
||||
private static class ProgressInfo {
|
||||
|
||||
// HORIZONTALスタイルの場合、初期メッセージがないと後からメッセージを指定しても表示されない
|
||||
@NonNull String message = " ";
|
||||
|
||||
boolean isIndeterminate = true;
|
||||
int value = 0;
|
||||
int max = 1;
|
||||
}
|
||||
|
||||
@NonNull private WeakReference< Activity > refActivity;
|
||||
@NonNull protected Handler handler;
|
||||
@NonNull protected final TootApiClient client;
|
||||
@NonNull private final ProgressInfo info = new ProgressInfo();
|
||||
@Nullable private ProgressDialog progress;
|
||||
@Nullable private String progress_prefix;
|
||||
|
||||
private final int progress_style;
|
||||
private boolean isAlive = true;
|
||||
private long last_message_shown;
|
||||
|
||||
private static final NumberFormat percent_format;
|
||||
|
||||
static{
|
||||
percent_format = NumberFormat.getPercentInstance();
|
||||
percent_format.setMaximumFractionDigits( 0 );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, SavedAccount access_info, int progress_style ){
|
||||
public TootApiTask( @NonNull Activity _activity, int progress_style ){
|
||||
this.refActivity = new WeakReference<>( _activity );
|
||||
this.handler = new Handler();
|
||||
this.client = new TootApiClient( _activity, this );
|
||||
this.progress_style = progress_style;
|
||||
if( progress_style != PROGRESS_NONE ){
|
||||
// ダイアログの遅延表示を実装したけど、すぐにダイアログを出した方が下のUIのタッチ判定を隠せて良いので使わないんだ…
|
||||
// handler.postDelayed( proc_progress_opener ,1000L );
|
||||
proc_progress_opener.run();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( @NonNull Activity _activity, SavedAccount access_info, int progress_style ){
|
||||
this( _activity, progress_style );
|
||||
client.setAccount( access_info );
|
||||
showProgress( _activity, progress_style );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, String instance, boolean bShowProgress ){
|
||||
this( _activity, instance, bShowProgress ? PROGRESS_SPINNER : PROGRESS_NONE );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, String instance, int progress_style ){
|
||||
this.client = new TootApiClient( _activity, this );
|
||||
public TootApiTask( @NonNull Activity _activity, String instance, int progress_style ){
|
||||
this( _activity, progress_style );
|
||||
client.setInstance( instance );
|
||||
showProgress( _activity, progress_style );
|
||||
}
|
||||
|
||||
private static int getDefaultProgressStyle( boolean bShowProgress ){
|
||||
return bShowProgress ? PROGRESS_SPINNER : PROGRESS_NONE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, boolean bShowProgress ){
|
||||
this( _activity, bShowProgress ? PROGRESS_SPINNER : PROGRESS_NONE );
|
||||
public TootApiTask( @NonNull Activity _activity, boolean bShowProgress ){
|
||||
this( _activity, getDefaultProgressStyle( bShowProgress ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( Activity _activity, int progress_style ){
|
||||
this.client = new TootApiClient( _activity, this );
|
||||
showProgress( _activity, progress_style );
|
||||
public TootApiTask( @NonNull Activity _activity, SavedAccount access_info, boolean bShowProgress ){
|
||||
this( _activity, access_info, getDefaultProgressStyle( bShowProgress ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public TootApiTask( @NonNull Activity _activity, String instance, boolean bShowProgress ){
|
||||
this( _activity, instance, getDefaultProgressStyle( bShowProgress ) );
|
||||
}
|
||||
|
||||
public TootApiTask setProgressPrefix( String s ){
|
||||
|
@ -67,39 +107,7 @@ public abstract class TootApiTask extends AsyncTask< Void, Void, TootApiResult >
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
@Override public boolean isApiCancelled(){
|
||||
return isCancelled();
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( final String s ){
|
||||
if( progress != null ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setIndeterminate( true );
|
||||
if( ! TextUtils.isEmpty( progress_prefix ) ){
|
||||
progress.setMessage( progress_prefix + "\n" + s );
|
||||
}else{
|
||||
progress.setMessage( s );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public void publishApiProgressRatio( final int value, final int max ){
|
||||
if( progress != null ){
|
||||
Utils.runOnMainThread( new Runnable() {
|
||||
@Override public void run(){
|
||||
progress.setIndeterminate( false );
|
||||
progress.setProgress( value );
|
||||
progress.setMax( max );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// implements AsyncTask
|
||||
|
||||
@Override protected abstract TootApiResult doInBackground( Void... voids );
|
||||
|
||||
|
@ -115,36 +123,39 @@ public abstract class TootApiTask extends AsyncTask< Void, Void, TootApiResult >
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// implements TootApiClient.Callback
|
||||
|
||||
private ProgressDialog progress;
|
||||
private String progress_prefix;
|
||||
|
||||
private void showProgress( Activity activity, int progressStyle ){
|
||||
|
||||
if( progressStyle == PROGRESS_NONE ) return;
|
||||
|
||||
//noinspection deprecation
|
||||
this.progress = new ProgressDialog( activity );
|
||||
progress.setCancelable( true );
|
||||
progress.setProgressStyle( progressStyle );
|
||||
progress.setIndeterminate( true );
|
||||
progress.setMax( 1 );
|
||||
if( ! TextUtils.isEmpty( progress_prefix ) ){
|
||||
progress.setMessage( progress_prefix );
|
||||
}else{
|
||||
// HORIZONTALスタイルの場合、初期メッセージがないと後からメッセージを指定しても表示されない
|
||||
progress.setMessage( " " );
|
||||
}
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel( DialogInterface dialog ){
|
||||
TootApiTask.this.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.show();
|
||||
@Override public boolean isApiCancelled(){
|
||||
return isCancelled();
|
||||
}
|
||||
|
||||
@Override public void publishApiProgress( final String s ){
|
||||
synchronized( this ){
|
||||
info.message = s;
|
||||
info.isIndeterminate = true;
|
||||
}
|
||||
requestShowMessage();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// 内蔵メディアビューアのローディング進捗の表示に使う
|
||||
|
||||
public void publishApiProgressRatio( final int value, final int max ){
|
||||
synchronized( this ){
|
||||
info.isIndeterminate = false;
|
||||
info.value = value;
|
||||
info.max = max;
|
||||
}
|
||||
requestShowMessage();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
// ダイアログを閉じる
|
||||
private void dismissProgress(){
|
||||
synchronized( this ){
|
||||
isAlive = false;
|
||||
}
|
||||
if( progress != null ){
|
||||
try{
|
||||
progress.dismiss();
|
||||
|
@ -163,4 +174,73 @@ public abstract class TootApiTask extends AsyncTask< Void, Void, TootApiResult >
|
|||
}
|
||||
}
|
||||
|
||||
// ダイアログを開く
|
||||
private final Runnable proc_progress_opener = new Runnable() {
|
||||
@Override public void run(){
|
||||
synchronized( this ){
|
||||
Activity activity = refActivity.get();
|
||||
if( isAlive
|
||||
&& activity != null
|
||||
&& progress == null
|
||||
&& progress_style != PROGRESS_NONE
|
||||
){
|
||||
//noinspection deprecation
|
||||
progress = new ProgressDialog( activity );
|
||||
progress.setCancelable( true );
|
||||
progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
|
||||
@Override public void onCancel( DialogInterface dialog ){
|
||||
TootApiTask.this.cancel( true );
|
||||
}
|
||||
} );
|
||||
progress.setProgressStyle( progress_style );
|
||||
showProgressMessage();
|
||||
progress.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ダイアログのメッセージを更新する
|
||||
private void showProgressMessage(){
|
||||
if( progress == null ) return;
|
||||
|
||||
if( ! TextUtils.isEmpty( progress_prefix ) ){
|
||||
progress.setMessage( progress_prefix + ( TextUtils.isEmpty( info.message.trim() ) ? "" : "\n" + info.message ) );
|
||||
}else{
|
||||
progress.setMessage( info.message );
|
||||
}
|
||||
|
||||
progress.setIndeterminate( info.isIndeterminate );
|
||||
if( info.isIndeterminate ){
|
||||
progress.setProgressNumberFormat( null );
|
||||
progress.setProgressPercentFormat( null );
|
||||
}else{
|
||||
progress.setProgress( info.value );
|
||||
progress.setMax( info.max );
|
||||
progress.setProgressNumberFormat( "%1$,d / %2$,d" );
|
||||
progress.setProgressPercentFormat( percent_format );
|
||||
}
|
||||
|
||||
last_message_shown = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
// 少し後にダイアログのメッセージを更新する
|
||||
// あまり頻繁に更新せず、しかし繰り返し呼ばれ続けても時々は更新したい
|
||||
private void requestShowMessage(){
|
||||
long wait = 100L + last_message_shown - SystemClock.elapsedRealtime();
|
||||
wait = wait < 0L ? 0L : wait > 100L ? 100L : wait;
|
||||
handler.removeCallbacks( proc_progress_message );
|
||||
handler.postDelayed( proc_progress_message, wait );
|
||||
}
|
||||
|
||||
private final Runnable proc_progress_message = new Runnable() {
|
||||
@Override public void run(){
|
||||
synchronized( this ){
|
||||
if( progress != null && progress.isShowing() ){
|
||||
showProgressMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public class CustomEmojiCache {
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull final ConcurrentHashMap< String, CacheItem > cache = new ConcurrentHashMap<>();
|
||||
@NonNull final ConcurrentHashMap< String, CacheItem > cache;
|
||||
|
||||
////////////////////////////////
|
||||
// リクエスト
|
||||
|
@ -106,6 +106,14 @@ public class CustomEmojiCache {
|
|||
|
||||
cancelRequest( target_tag );
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if( cache == null ){
|
||||
// java.lang.NullPointerException:
|
||||
// at java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:915)
|
||||
// at jp.juggler.subwaytooter.util.CustomEmojiCache.get (CustomEmojiCache.java:113)
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized( cache ){
|
||||
long now = getNow();
|
||||
|
||||
|
@ -122,10 +130,10 @@ public class CustomEmojiCache {
|
|||
if( time_error != null && now < time_error + ERROR_EXPIRE ){
|
||||
return null;
|
||||
}
|
||||
}catch(Throwable ex){
|
||||
}catch( Throwable ex ){
|
||||
// NullPointerException at java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:915)
|
||||
|
||||
log.trace(ex);
|
||||
log.trace( ex );
|
||||
}
|
||||
}
|
||||
synchronized( queue ){
|
||||
|
@ -143,6 +151,7 @@ public class CustomEmojiCache {
|
|||
public CustomEmojiCache( Context context ){
|
||||
this.context = context;
|
||||
this.handler = new Handler( context.getMainLooper() );
|
||||
this.cache = new ConcurrentHashMap<>();
|
||||
this.worker = new Worker();
|
||||
worker.start();
|
||||
}
|
||||
|
@ -166,7 +175,7 @@ public class CustomEmojiCache {
|
|||
}
|
||||
|
||||
if( request == null ){
|
||||
if(DEBUG) log.d( "wait. req_size=%d", req_size );
|
||||
if( DEBUG ) log.d( "wait. req_size=%d", req_size );
|
||||
waitEx( 86400000L );
|
||||
continue;
|
||||
}
|
||||
|
@ -175,8 +184,19 @@ public class CustomEmojiCache {
|
|||
continue;
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if( cache == null ){
|
||||
// Fujitsu F-01H(F01H), 2048MB RAM, Android 6.0
|
||||
// java.lang.NullPointerException:
|
||||
// at java.util.concurrent.ConcurrentHashMap.get (ConcurrentHashMap.java:772)
|
||||
// at jp.juggler.subwaytooter.util.CustomEmojiCache$Worker.run (CustomEmojiCache.java:183)
|
||||
waitEx( 1000L );
|
||||
continue;
|
||||
}
|
||||
|
||||
long now = getNow();
|
||||
int cache_size;
|
||||
|
||||
synchronized( cache ){
|
||||
|
||||
// 成功キャッシュ
|
||||
|
@ -197,13 +217,14 @@ public class CustomEmojiCache {
|
|||
//noinspection UnusedAssignment
|
||||
cache_size = cache.size();
|
||||
}
|
||||
if(DEBUG) log.d( "start get image. req_size=%d, cache_size=%d url=%s", req_size, cache_size, request.url );
|
||||
if( DEBUG )
|
||||
log.d( "start get image. req_size=%d, cache_size=%d url=%s", req_size, cache_size, request.url );
|
||||
|
||||
APNGFrames frames = null;
|
||||
try{
|
||||
byte[] data = App1.getHttpCached( request.url );
|
||||
if( data == null ){
|
||||
log.e("get failed. url=%s",request.url );
|
||||
log.e( "get failed. url=%s", request.url );
|
||||
}else{
|
||||
frames = decodeAPNG( data, request.url );
|
||||
}
|
||||
|
@ -269,10 +290,10 @@ public class CustomEmojiCache {
|
|||
try{
|
||||
APNGFrames frames = APNGFrames.parseAPNG( new ByteArrayInputStream( data ), 64 );
|
||||
if( frames == null ){
|
||||
if(DEBUG) log.d("parseAPNG returns null.");
|
||||
if( DEBUG ) log.d( "parseAPNG returns null." );
|
||||
// fall thru
|
||||
}else if( frames.isSingleFrame() ){
|
||||
if(DEBUG) log.d( "parseAPNG returns single frame." );
|
||||
if( DEBUG ) log.d( "parseAPNG returns single frame." );
|
||||
// mastodonのstatic_urlが返すPNG画像はAPNGだと透明になってる場合がある。BitmapFactoryでデコードしなおすべき
|
||||
frames.dispose();
|
||||
// fall thru
|
||||
|
@ -288,7 +309,7 @@ public class CustomEmojiCache {
|
|||
try{
|
||||
Bitmap b = decodeBitmap( data, 128 );
|
||||
if( b != null ){
|
||||
if(DEBUG) log.d("bitmap decoded.");
|
||||
if( DEBUG ) log.d( "bitmap decoded." );
|
||||
return new APNGFrames( b );
|
||||
}else{
|
||||
log.e( "Bitmap decode returns null. %s", url );
|
||||
|
|
Loading…
Reference in New Issue