From 02ff94b22bfc2269496521e1a911d3e4f7126456 Mon Sep 17 00:00:00 2001 From: tateisu Date: Sat, 25 Mar 2017 04:27:33 +0900 Subject: [PATCH] =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E6=83=85=E5=A0=B1=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=AB=E4=B8=80?= =?UTF-8?q?=E6=99=82=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=8CMesiaScan?= =?UTF-8?q?ner=E3=81=AB=E4=BD=BF=E3=82=8F=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../juggler/fadownloader/DownloadService.java | 7 ++ .../juggler/fadownloader/DownloadWorker.java | 97 ++++++++++++------- .../jp/juggler/fadownloader/LocalFile.java | 11 +++ .../fadownloader/MediaScannerTracker.java | 97 +++++++++++++++++++ .../java/jp/juggler/fadownloader/Utils.java | 4 +- 5 files changed, 182 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/jp/juggler/fadownloader/MediaScannerTracker.java diff --git a/app/src/main/java/jp/juggler/fadownloader/DownloadService.java b/app/src/main/java/jp/juggler/fadownloader/DownloadService.java index 78f60a6..9c5505c 100644 --- a/app/src/main/java/jp/juggler/fadownloader/DownloadService.java +++ b/app/src/main/java/jp/juggler/fadownloader/DownloadService.java @@ -57,6 +57,7 @@ public class DownloadService extends Service{ NotificationManager mNotificationManager; WorkerTracker worker_tracker; + MediaScannerTracker media_tracker; @Override public void onCreate(){ super.onCreate(); @@ -95,6 +96,8 @@ public class DownloadService extends Service{ } } ); + media_tracker = new MediaScannerTracker(this,log); + wifi_tracker = new NetworkTracker( this, log, new NetworkTracker.Callback(){ @Override public void onConnectionEvent( boolean is_connected ,String cause){ if( is_connected ){ @@ -115,7 +118,11 @@ public class DownloadService extends Service{ worker_tracker.dispose(); + + media_tracker.dispose(); + location_tracker.dispose(); + wifi_tracker.dispose(); if( mGoogleApiClient.isConnected() ){ diff --git a/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java b/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java index e91b848..9fa0ff4 100644 --- a/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java +++ b/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.location.Location; -import android.media.MediaScannerConnection; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkInfo; @@ -18,12 +17,15 @@ import android.support.annotation.NonNull; import android.text.TextUtils; +import org.apache.commons.io.IOUtils; + import it.sephiroth.android.library.exif2.ExifInterface; import jp.juggler.fadownloader.targets.FlashAir; import jp.juggler.fadownloader.targets.PentaxKP; import jp.juggler.fadownloader.targets.PqiAirCard; import java.io.File; +import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; @@ -233,18 +235,29 @@ public static class ErrorAndMessage{ @NonNull public ErrorAndMessage updateFileLocation( final Location location, ScanItem item ){ ErrorAndMessage em = null; + boolean bDeleteTempFile = false; + LocalFile local_temp =null; try{ - LocalFile file = item.local_file; - - LocalFile tmp_path = new LocalFile( file.getParent(), "tmp-" + currentThread().getId() + "-" + android.os.Process.myPid() + "-" + file.getName() ); - if( ! tmp_path.prepareFile( log, true ,item.mime_type) ){ - return new ErrorAndMessage( true, "file creation failed." ); + LocalFile local_file = item.local_file; + LocalFile local_parent = local_file.getParent(); + + // MediaScannerが一時ファイルを勝手にスキャンしてしまう + // DocumentFile はMIME TYPE に合わせてファイル拡張子を変えてしまう + // なので、一時ファイルの拡張子とMIME TYPE は無害なものに設定するしかない + String tmp_name = local_file.getName()+ ".tmp-" + currentThread().getId() + "-" + android.os.Process.myPid(); + local_temp = new LocalFile( local_parent, tmp_name ); + + if( ! local_temp.prepareFile( log,true,Utils.MIME_TYPE_APPLICATION_OCTET_STREAM )){ + log.e("prepareFile() failed."); + return new ErrorAndMessage( false,"prepareFile() failed." ); } + bDeleteTempFile = true; + try{ - OutputStream os = tmp_path.openOutputStream( service ); + OutputStream os = local_temp.openOutputStream( service ); try{ - InputStream is = file.openInputStream( service ); + InputStream is = item.local_file.openInputStream( service ); try{ ExifInterface.modifyExifTag( is, ExifInterface.Options.OPTION_ALL , os, new ExifInterface.ModifyExifTagCallback(){ @@ -284,30 +297,60 @@ public static class ErrorAndMessage{ em = new ErrorAndMessage( true, LogWriter.formatError( ex, "exif mangling failed." ) ); } - if( em != null ){ - tmp_path.delete(); - return em; - } + if( em != null ) return em; // 更新後の方がファイルが小さいことがあるのか? - if( tmp_path.length( log ) < file.length( log ) ){ + if( local_temp.length(log) < local_file.length(log) ){ log.e( "EXIF付与したファイルの方が小さい!付与前後のファイルを残しておく" ); // この場合両方のファイルを残しておく + bDeleteTempFile = false; return new ErrorAndMessage( true, "EXIF付与したファイルの方が小さい" ); } - if( ! file.delete() || ! tmp_path.renameTo( file.getName() ) ){ - log.e( "EXIF追加後のファイル操作に失敗" ); - return new ErrorAndMessage( true, "EXIF追加後のファイル操作に失敗" ); + // DocumentFile にはsetMimeType が存在しないから + // 一時ファイルをrename してもMIME TYPEを補正できない + // 仕方ないので元のファイルを上書きする + // 更新後に再度MediaScannerでスキャンし直すのでなんとかなるだろう。。。 + + try{ + InputStream is = local_temp.openInputStream( service ); + try{ + OutputStream os = local_file.openOutputStream( service ); + try{ + IOUtils.copy( is, os ); + }finally{ + try{ + os.close(); + }catch( Throwable ignored ){ + } + } + }finally{ + try{ + is.close(); + }catch( Throwable ignored ){ + } + } + }catch(Throwable ex){ + ex.printStackTrace(); + log.e( ex, "file copy failed." ); + return new ErrorAndMessage( false, "file copy failed." ); } - log.i( "%s に位置情報を付与しました", file.getName() ); + log.i( "%s に位置情報を付与しました", local_file.getName() ); return new ErrorAndMessage( false, "embedded" ); }catch( Throwable ex ){ ex.printStackTrace(); log.e( ex, "exif mangling failed." ); return new ErrorAndMessage( true, LogWriter.formatError( ex, "exif mangling failed." ) ); + }finally{ + if( local_temp != null && bDeleteTempFile ){ + try{ + //noinspection ResultOfMethodCallIgnored + local_temp.delete(); + }catch( Throwable ignored ){ + } + } } } @@ -381,6 +424,7 @@ public void afterDownload( long time_start, byte[] data, ScanItem item ){ Location location = callback.getLocation(); if( location != null && DownloadWorker.reJPEG.matcher( item.name ).find() ){ + DownloadWorker.ErrorAndMessage em = updateFileLocation( location, item ); if( item.time > 0L ) item.local_file.setFileTime( service, log, item.time ); @@ -391,7 +435,8 @@ public void afterDownload( long time_start, byte[] data, ScanItem item ){ , "GeoTagging: " + em.message ); - setMediaScanner( item ); + service.media_tracker.addFile( item.local_file.getFile(service,log),item.mime_type ); + }else{ if( item.time > 0L ) item.local_file.setFileTime( service, log, item.time ); @@ -401,26 +446,12 @@ public void afterDownload( long time_start, byte[] data, ScanItem item ){ , DownloadRecord.STATE_COMPLETED , "OK" ); - setMediaScanner( item ); + service.media_tracker.addFile( item.local_file.getFile(service,log),item.mime_type ); } } - } - private void setMediaScanner( ScanItem item ){ - if( item.mime_type != null ){ - File file = item.local_file.getFile(service,log); - if( file != null ){ - MediaScannerConnection.scanFile( - service - ,new String[]{ file.getAbsolutePath() } - ,new String[]{ item.mime_type} - ,null - ); - } - } - } public void onFileScanStart(){ job_queue = new ScanItem.Queue(); diff --git a/app/src/main/java/jp/juggler/fadownloader/LocalFile.java b/app/src/main/java/jp/juggler/fadownloader/LocalFile.java index 219ecda..d6f5cef 100644 --- a/app/src/main/java/jp/juggler/fadownloader/LocalFile.java +++ b/app/src/main/java/jp/juggler/fadownloader/LocalFile.java @@ -196,6 +196,17 @@ public long length( LogWriter log ){ return 0L; } + public boolean isFile( LogWriter log ){ + if( prepareFile( log, false,null ) ){ + if( Build.VERSION.SDK_INT >= DOCUMENT_FILE_VERSION ){ + return ( (DocumentFile) local_file ).isFile(); + }else{ + return ( (File) local_file ).isFile(); + } + } + return false; + } + public OutputStream openOutputStream( Context context ) throws FileNotFoundException{ if( Build.VERSION.SDK_INT >= DOCUMENT_FILE_VERSION ){ Uri file_uri = ( (DocumentFile) local_file ).getUri(); diff --git a/app/src/main/java/jp/juggler/fadownloader/MediaScannerTracker.java b/app/src/main/java/jp/juggler/fadownloader/MediaScannerTracker.java new file mode 100644 index 0000000..ed0770e --- /dev/null +++ b/app/src/main/java/jp/juggler/fadownloader/MediaScannerTracker.java @@ -0,0 +1,97 @@ +package jp.juggler.fadownloader; + +import android.content.Context; +import android.media.MediaScannerConnection; +import android.net.Uri; +import android.os.Handler; +import android.os.SystemClock; +import android.text.TextUtils; + +import java.io.File; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class MediaScannerTracker implements MediaScannerConnection.MediaScannerConnectionClient{ + + @Override public void onMediaScannerConnected(){ + handler.post( queue_reader ); + } + + @Override public void onScanCompleted( String path, Uri uri ){ + } + + final Context context; + final LogWriter log; + final MediaScannerConnection conn; + final Handler handler; + long last_connect_start; + + volatile boolean is_dispose = false; + + void dispose(){ + is_dispose = true; + } + + public MediaScannerTracker( Context context, LogWriter log ){ + this.context = context; + this.log = log; + this.conn = new MediaScannerConnection( context, this ); + this.handler = new Handler(); + prepareConnection(); + } + + static class Item{ + + String path; + String mime_type; + } + + final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + + public void addFile( File file, String mime_type ){ + if( file == null || ! file.isFile() ) return; + if( TextUtils.isEmpty( mime_type ) ) return; + Item item = new Item(); + item.path = file.getAbsolutePath(); + item.mime_type = mime_type; + queue.add( item ); + handler.post( queue_reader ); + } + + private boolean prepareConnection(){ + if( conn.isConnected() ) return true; + + long now = SystemClock.elapsedRealtime(); + if( now - last_connect_start >= 5000L ){ + last_connect_start = now; + conn.connect(); + } + return false; + } + + final Runnable queue_reader = new Runnable(){ + @Override public void run(){ + handler.removeCallbacks( queue_reader ); + for( ; ; ){ + + Item item = queue.peek(); + + if( item == null ){ + if( is_dispose ){ + conn.disconnect(); + } + break; + } + + if( ! prepareConnection() ){ + handler.postDelayed( queue_reader, 1000L ); + break; + } + + conn.scanFile( item.path, item.mime_type ); + + queue.poll(); + } + } + }; + +} diff --git a/app/src/main/java/jp/juggler/fadownloader/Utils.java b/app/src/main/java/jp/juggler/fadownloader/Utils.java index 6f2d1b7..491764b 100644 --- a/app/src/main/java/jp/juggler/fadownloader/Utils.java +++ b/app/src/main/java/jp/juggler/fadownloader/Utils.java @@ -496,6 +496,8 @@ static String findMimeTypeEx(String ext){ } } + public static final String MIME_TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream"; + public static String getMimeType( LogWriter log, String src ){ String ext = MimeTypeMap.getFileExtensionFromUrl( src ); if( !TextUtils.isEmpty( ext ) ){ @@ -513,7 +515,7 @@ public static String getMimeType( LogWriter log, String src ){ if( mime_type == null && log != null ) log.w("getMimeType(): unknown file extension '%s'",ext); } - return "application/octet-stream"; + return MIME_TYPE_APPLICATION_OCTET_STREAM; } static class FileInfo{