diff --git a/app/build.gradle b/app/build.gradle
index 27a518a..2abcdfc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,10 +5,10 @@ android {
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "jp.juggler.fadownloader"
- minSdkVersion 21
+ minSdkVersion 14
targetSdkVersion 25
- versionCode 6
- versionName "1.3"
+ versionCode 8
+ versionName "1.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 13fcd91..0ef1802 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,6 +54,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/jp/juggler/fadownloader/ActMain.java b/app/src/main/java/jp/juggler/fadownloader/ActMain.java
index 34f474a..c9a6d1c 100644
--- a/app/src/main/java/jp/juggler/fadownloader/ActMain.java
+++ b/app/src/main/java/jp/juggler/fadownloader/ActMain.java
@@ -10,6 +10,7 @@
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.support.annotation.NonNull;
@@ -47,6 +48,8 @@
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
+import java.io.File;
+import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -61,6 +64,7 @@ public class ActMain
static final int REQUEST_CODE_DOCUMENT = 2;
static final int REQUEST_CHECK_SETTINGS = 3;
static final int REQUEST_PURCHASE = 4;
+ static final int REQUEST_FOLDER_PICKER = 5;
TextView tvStatus;
@@ -165,15 +169,55 @@ public class ActMain
if( requestCode == REQUEST_CODE_DOCUMENT ){
if( resultCode == Activity.RESULT_OK ){
- Uri treeUri = resultData.getData();
- // 永続的な許可を取得
- getContentResolver().takePersistableUriPermission( treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
- // 覚えておく
- Pref.pref( this ).edit()
- .putString( Pref.UI_FOLDER_URI, treeUri.toString() )
- .apply();
+ if( Build.VERSION.SDK_INT >= 21){
+ try{
+ Uri treeUri = resultData.getData();
+ // 永続的な許可を取得
+ getContentResolver().takePersistableUriPermission( treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
+ // 覚えておく
+ Pref.pref( this ).edit()
+ .putString( Pref.UI_FOLDER_URI, treeUri.toString() )
+ .apply();
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ Toast.makeText(this,String.format("folder access failed. %s %s",ex.getClass().getSimpleName(),ex.getMessage()),Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ Page0 page = pager_adapter.getPage( 0 );
+ if( page != null ) page.folder_view_update();
+ return;
+ }else if ( requestCode == REQUEST_FOLDER_PICKER ){
+ if( resultCode == Activity.RESULT_OK ){
+ try{
+ String path = resultData.getStringExtra( FolderPicker.EXTRA_FOLDER );
+ String dummy = Thread.currentThread().getId()+"."+android.os.Process.myPid();
+ File test_dir = new File( new File( path ), dummy );
+ test_dir.mkdir();
+ try{
+ File test_file = new File( test_dir, dummy );
+ try{
+ FileOutputStream fos = new FileOutputStream( test_file );
+ try{
+ fos.write( Utils.encodeUTF8( "TEST" ) );
+ }finally{
+ fos.close();
+ }
+ }finally{
+ test_file.delete();
+ }
+ }finally{
+ test_dir.delete();
+ }
+ // 覚えておく
+ Pref.pref( this ).edit()
+ .putString( Pref.UI_FOLDER_URI, path )
+ .apply();
+ }catch(Throwable ex){
+ ex.printStackTrace( );
+ Toast.makeText(this,String.format("folder access failed. %s %s",ex.getClass().getSimpleName(),ex.getMessage()),Toast.LENGTH_LONG).show();
+ }
}
-
Page0 page = pager_adapter.getPage( 0 );
if( page != null ) page.folder_view_update();
return;
@@ -397,14 +441,18 @@ void startDownloadService(){
String folder_uri = null;
sv = pref.getString( Pref.UI_FOLDER_URI, null );
if( ! TextUtils.isEmpty( sv ) ){
- DocumentFile folder = DocumentFile.fromTreeUri( this, Uri.parse( sv ) );
- if( folder != null ){
- if( folder.exists() && folder.canWrite() ){
- folder_uri = sv;
+ if( Build.VERSION.SDK_INT >= 21 ){
+ DocumentFile folder = DocumentFile.fromTreeUri( this, Uri.parse( sv ) );
+ if( folder != null ){
+ if( folder.exists() && folder.canWrite() ){
+ folder_uri = sv;
+ }
}
+ }else{
+ folder_uri = sv;
}
}
- if( folder_uri == null ){
+ if( TextUtils.isEmpty( folder_uri ) ){
Toast.makeText( this, getString( R.string.folder_not_ok ), Toast.LENGTH_SHORT ).show();
return;
}
diff --git a/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java b/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java
index e4e6711..7e0d5a1 100644
--- a/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java
+++ b/app/src/main/java/jp/juggler/fadownloader/DownloadWorker.java
@@ -6,19 +6,18 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
+import android.net.ConnectivityManager;
import android.net.Network;
+import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
-import android.support.v4.provider.DocumentFile;
import it.sephiroth.android.library.exif2.ExifInterface;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
@@ -61,13 +60,13 @@ public DownloadWorker( DownloadService service, Intent intent, Callback callback
this.folder_uri = intent.getStringExtra( DownloadService.EXTRA_FOLDER_URI );
this.interval = intent.getIntExtra( DownloadService.EXTRA_INTERVAL, 86400 );
this.file_type = intent.getStringExtra( DownloadService.EXTRA_FILE_TYPE );
- boolean force_wifi = intent.getBooleanExtra( DownloadService.EXTRA_FORCE_WIFI ,false);
- String ssid =intent.getStringExtra( DownloadService.EXTRA_SSID );
+ boolean force_wifi = intent.getBooleanExtra( DownloadService.EXTRA_FORCE_WIFI, false );
+ String ssid = intent.getStringExtra( DownloadService.EXTRA_SSID );
LocationTracker.Setting location_setting = new LocationTracker.Setting();
- location_setting.interval_desired = intent.getLongExtra( DownloadService.EXTRA_LOCATION_INTERVAL_DESIRED ,LocationTracker.DEFAULT_INTERVAL_DESIRED);
- location_setting.interval_min = intent.getLongExtra( DownloadService.EXTRA_LOCATION_INTERVAL_MIN ,LocationTracker.DEFAULT_INTERVAL_MIN);
- location_setting.mode = intent.getIntExtra( DownloadService.EXTRA_LOCATION_MODE ,LocationTracker.DEFAULT_MODE);
+ location_setting.interval_desired = intent.getLongExtra( DownloadService.EXTRA_LOCATION_INTERVAL_DESIRED, LocationTracker.DEFAULT_INTERVAL_DESIRED );
+ location_setting.interval_min = intent.getLongExtra( DownloadService.EXTRA_LOCATION_INTERVAL_MIN, LocationTracker.DEFAULT_INTERVAL_MIN );
+ location_setting.mode = intent.getIntExtra( DownloadService.EXTRA_LOCATION_MODE, LocationTracker.DEFAULT_MODE );
Pref.pref( service ).edit()
.putBoolean( Pref.WORKER_REPEAT, repeat )
@@ -76,7 +75,7 @@ public DownloadWorker( DownloadService service, Intent intent, Callback callback
.putInt( Pref.WORKER_INTERVAL, interval )
.putString( Pref.WORKER_FILE_TYPE, file_type )
.putLong( Pref.WORKER_LOCATION_INTERVAL_DESIRED, location_setting.interval_desired )
- .putLong( Pref.WORKER_LOCATION_INTERVAL_MIN, location_setting.interval_min )
+ .putLong( Pref.WORKER_LOCATION_INTERVAL_MIN, location_setting.interval_min )
.putInt( Pref.WORKER_LOCATION_MODE, location_setting.mode )
.putBoolean( Pref.WORKER_FORCE_WIFI, force_wifi )
.putString( Pref.WORKER_SSID, ssid )
@@ -84,9 +83,9 @@ public DownloadWorker( DownloadService service, Intent intent, Callback callback
file_type_list = file_type_parse();
- service.wifi_tracker.updateSetting(force_wifi,ssid);
+ service.wifi_tracker.updateSetting( force_wifi, ssid );
- service.location_tracker.updateSetting(location_setting);
+ service.location_tracker.updateSetting( location_setting );
}
public DownloadWorker( DownloadService service, Callback callback ){
@@ -103,18 +102,18 @@ public DownloadWorker( DownloadService service, Callback callback ){
this.file_type = pref.getString( Pref.WORKER_FILE_TYPE, null );
boolean force_wifi = pref.getBoolean( Pref.WORKER_FORCE_WIFI, false );
- String ssid =pref.getString( Pref.WORKER_SSID, null );
+ String ssid = pref.getString( Pref.WORKER_SSID, null );
LocationTracker.Setting location_setting = new LocationTracker.Setting();
- location_setting.interval_desired = pref.getLong(Pref.WORKER_LOCATION_INTERVAL_DESIRED ,LocationTracker.DEFAULT_INTERVAL_DESIRED);
- location_setting.interval_min = pref.getLong(Pref.WORKER_LOCATION_INTERVAL_MIN,LocationTracker.DEFAULT_INTERVAL_MIN);
- location_setting.mode = pref.getInt(Pref.WORKER_LOCATION_MODE ,LocationTracker.DEFAULT_MODE);
+ location_setting.interval_desired = pref.getLong( Pref.WORKER_LOCATION_INTERVAL_DESIRED, LocationTracker.DEFAULT_INTERVAL_DESIRED );
+ location_setting.interval_min = pref.getLong( Pref.WORKER_LOCATION_INTERVAL_MIN, LocationTracker.DEFAULT_INTERVAL_MIN );
+ location_setting.mode = pref.getInt( Pref.WORKER_LOCATION_MODE, LocationTracker.DEFAULT_MODE );
file_type_list = file_type_parse();
- service.wifi_tracker.updateSetting(force_wifi,ssid);
+ service.wifi_tracker.updateSetting( force_wifi, ssid );
- service.location_tracker.updateSetting(location_setting);
+ service.location_tracker.updateSetting( location_setting );
}
final AtomicReference status = new AtomicReference<>( "?" );
@@ -173,88 +172,16 @@ void waitEx( long ms ){
}
}
- static class FilePathX{
-
- DocumentFile document_file;
- String name;
- FilePathX parent;
- ArrayList file_list;
-
- public ArrayList getFileList(){
- if( document_file != null ){
- if( file_list != null ) return file_list;
- ArrayList result = new ArrayList<>();
- Collections.addAll( result, document_file.listFiles() );
- Collections.sort( result, new Comparator(){
- @Override public int compare( DocumentFile a, DocumentFile b ){
- return a.getName().compareTo( b.getName() );
- }
- } );
- return file_list = result;
- }else if( parent != null ){
- ArrayList parent_childs = parent.getFileList();
- if( parent_childs != null ){
- DocumentFile file = bsearch( parent_childs, name );
- if( file != null ){
- this.document_file = file;
- return getFileList();
- }
- }
- }
- return null;
- }
-
- private DocumentFile prepareDirectory( LogWriter log ){
- try{
- if( document_file != null ) return document_file;
- if( parent != null ){
- DocumentFile parent_dir = parent.prepareDirectory( log );
- if( parent_dir == null ) return null;
-
- ArrayList parent_list = parent.getFileList();
- DocumentFile file = bsearch( parent_list, name );
- if( file == null ){
- log.i( R.string.folder_create, name );
- file = parent_dir.createDirectory( name );
- }
- return document_file = file;
- }
- }catch( Throwable ex ){
- log.e( R.string.folder_create_failed, ex.getClass().getSimpleName(), ex.getMessage() );
- }
- return null;
- }
-
- public DocumentFile prepareFile( LogWriter log ){
- try{
- if( document_file != null ) return document_file;
- if( parent != null ){
- DocumentFile parent_dir = parent.prepareDirectory( log );
- if( parent_dir == null ) return null;
-
- DocumentFile file = bsearch( parent.getFileList(), name );
- if( file == null ){
- file = parent_dir.createFile( "application/octet-stream", name );
- }
- return document_file = file;
- }
- }catch( Throwable ex ){
- log.e( R.string.file_create_failed, ex.getClass().getSimpleName(), ex.getMessage() );
- }
- return null;
- }
- }
-
static class Item{
final String air_path;
- final FilePathX local_path;
+ final FilePathX local_file;
final boolean is_file;
final long size;
- Item( String air_path, FilePathX local_path, boolean is_file, long size ){
+ Item( String air_path, FilePathX local_file, boolean is_file, long size ){
this.air_path = air_path;
- this.local_path = local_path;
+ this.local_file = local_file;
this.is_file = is_file;
this.size = size;
}
@@ -263,23 +190,6 @@ static class Item{
static final Pattern reLine = Pattern.compile( "([^\\x0d\\x0a]+)" );
static final Pattern reAttr = Pattern.compile( ",(\\d+),(\\d+),(\\d+),(\\d+)$" );
- private static DocumentFile bsearch( ArrayList local_files, String fname ){
- int start = 0;
- int end = local_files.size();
- while( ( end - start ) > 0 ){
- int mid = ( ( start + end ) >> 1 );
- DocumentFile x = local_files.get( mid );
- int i = fname.compareTo( x.getName() );
- if( i < 0 ){
- end = mid;
- }else if( i > 0 ){
- start = mid + 1;
- }else{
- return x;
- }
- }
- return null;
- }
@Override public void run(){
@@ -287,7 +197,6 @@ private static DocumentFile bsearch( ArrayList local_files, String
boolean allow_stop_service = false;
-
callback.onThreadStart();
while( ! isCancelled() ){
@@ -349,10 +258,10 @@ private static DocumentFile bsearch( ArrayList local_files, String
status.set( service.getString( R.string.wifi_check ) );
// 通信の安定を確認
- Network network = null;
long network_check_start = SystemClock.elapsedRealtime();
+ Object network = null;
while( ! isCancelled() ){
- network = Utils.getWiFiNetwork( service );
+ network = (Object) getWiFiNetwork( service );
if( network != null ) break;
//
long er_now = SystemClock.elapsedRealtime();
@@ -405,8 +314,7 @@ private static DocumentFile bsearch( ArrayList local_files, String
// フォルダを探索する
final LinkedList- job_queue = new LinkedList<>();
{
- FilePathX local_path = new FilePathX();
- local_path.document_file = DocumentFile.fromTreeUri( service, Uri.parse( folder_uri ) );
+ FilePathX local_path = new FilePathX(service,folder_uri);
job_queue.add( new Item( "/", local_path, false, 0L ) );
}
boolean has_error = false;
@@ -466,8 +374,8 @@ private static DocumentFile bsearch( ArrayList local_files, String
int time = Integer.parseInt( mAttr.group( 4 ), 10 );
// https://flashair-developers.com/ja/support/forum/#/discussion/3/%E3%82%AB%E3%83%B3%E3%83%9E%E5%8C%BA%E5%88%87%E3%82%8A
- String dir = (item.air_path.equals( "/" )? "": item.air_path);
- String fname = line.substring( dir.length() + 1, mAttr.start() );
+ String dir = ( item.air_path.equals( "/" ) ? "" : item.air_path );
+ String file_name = line.substring( dir.length() + 1, mAttr.start() );
if( ( attr & 2 ) != 0 ){
// skip hidden file
@@ -477,11 +385,9 @@ private static DocumentFile bsearch( ArrayList local_files, String
continue;
}
- String child_air_path = dir + "/" + fname;
+ String child_air_path = dir + "/" + file_name;
- FilePathX local_child = new FilePathX();
- local_child.parent = item.local_path;
- local_child.name = fname;
+ final FilePathX local_child = new FilePathX(item.local_file,file_name);
if( ( attr & 0x10 ) != 0 ){
// サブフォルダはキューに追加する
@@ -495,7 +401,7 @@ private static DocumentFile bsearch( ArrayList local_files, String
// file type matching
boolean bMatch = false;
for( Pattern re : file_type_list ){
- if( ! re.matcher( fname ).find() ) continue;
+ if( ! re.matcher( file_name ).find() ) continue;
bMatch = true;
break;
}
@@ -503,25 +409,23 @@ private static DocumentFile bsearch( ArrayList local_files, String
continue;
}
- DocumentFile file = local_child.prepareFile( log );
- if( file == null ){
- log.e( "%s//%s :skip. can not prepare local file.", item.air_path, fname );
+ if( ! local_child.prepareFile( log ) ){
+ log.e( "%s//%s :skip. can not prepare local file.", item.air_path, file_name );
continue;
- }else if( file.length() >= size ){
+ }else if( local_child.length() >= size ){
// log.f( "%s//%s :skip. same file size.",item.air_path, fname );
continue;
}
status.set( service.getString( R.string.download_file, child_air_path ) );
- final Uri file_uri = file.getUri();
final String get_url = flashair_url + Uri.encode( child_air_path );
data = client.getHTTP( log, network, get_url, new HTTPClientReceiver(){
final byte[] buf = new byte[ 2048 ];
public byte[] onHTTPClientStream( LogWriter log, CancelChecker cancel_checker, InputStream in, int content_length ){
try{
- OutputStream os = service.getContentResolver().openOutputStream( file_uri );
+ OutputStream os = local_child.openOutputStream(service);
if( os == null ){
log.e( "cannot open local output file." );
}else{
@@ -554,7 +458,7 @@ public byte[] onHTTPClientStream( LogWriter log, CancelChecker cancel_checker, I
if( isCancelled() ){
// no log.
}else if( data == null ){
- log.e( "FILE %s :HTTP error %s", fname, client.last_error );
+ log.e( "FILE %s :HTTP error %s", file_name, client.last_error );
if( client.last_error.contains( "UnknownHostException" ) ){
client.last_error = service.getString( R.string.flashair_host_error );
@@ -564,13 +468,13 @@ public byte[] onHTTPClientStream( LogWriter log, CancelChecker cancel_checker, I
has_error = true;
}else{
- log.i( "FILE %s :download complete. %dms", fname, SystemClock.elapsedRealtime() - time_start );
+ log.i( "FILE %s :download complete. %dms", file_name, SystemClock.elapsedRealtime() - time_start );
// 位置情報を取得する時にファイルの日時が使えないかと思ったけど
// タイムゾーンがわからん…
Location location = callback.getLocation();
- if( location != null && reJPEG.matcher( fname ).find() ){
+ if( location != null && reJPEG.matcher( file_name ).find() ){
status.set( service.getString( R.string.exif_update, child_air_path ) );
updateFileLocation( location, local_child );
}
@@ -593,16 +497,15 @@ public byte[] onHTTPClientStream( LogWriter log, CancelChecker cancel_checker, I
private void updateFileLocation( final Location location, FilePathX file ){
try{
- FilePathX tmp_path = new FilePathX();
- tmp_path.parent = file.parent;
- tmp_path.name = "tmp-" + currentThread().getId() + "-" + android.os.Process.myPid() + "-" +file.name;
+ FilePathX tmp_path = new FilePathX(file.parent,"tmp-" + currentThread().getId() + "-" + android.os.Process.myPid() + "-" + file.name);
- DocumentFile tmp_file = tmp_path.prepareFile( log );
- if( tmp_file != null ){
+ if(!tmp_path.prepareFile( log )){
+ throw new RuntimeException( "create file failed." );
+ }else{
boolean bModifyFailed = false;
- OutputStream os = service.getContentResolver().openOutputStream( tmp_file.getUri() );
+ OutputStream os = tmp_path.openOutputStream( service );
try{
- InputStream is = service.getContentResolver().openInputStream( file.document_file.getUri() );
+ InputStream is = file.openInputStream(service);
try{
ExifInterface.modifyExifTag( is, ExifInterface.Options.OPTION_ALL
, os, new ExifInterface.ModifyExifTagCallback(){
@@ -639,27 +542,22 @@ private void updateFileLocation( final Location location, FilePathX file ){
}
}
-
-
if( bModifyFailed ){
- try{
- tmp_file.delete();
- }catch( Throwable ignored ){
+ tmp_path.delete();
- }
}else{
try{
// 更新後の方がファイルが小さいことがあるのか?
- if(tmp_file.length() < file.document_file.length() ){
- log.e("EXIF付与したファイルの方が小さい!付与前後のファイルを残しておく");
+ if( tmp_path.length() < file.length() ){
+ log.e( "EXIF付与したファイルの方が小さい!付与前後のファイルを残しておく" );
// この場合両方のファイルを残しておく
}else{
- if( ! file.document_file.delete() ){
+ if( ! file.delete() ){
log.e( "EXIF追加後のファイル操作に失敗" );
- }else if( ! tmp_file.renameTo( file.name ) ){
+ }else if( ! tmp_path.renameTo( file.name ) ){
log.e( "EXIF追加後のファイル操作に失敗" );
}else{
- log.i("%s に位置情報を付与しました",file.name );
+ log.i( "%s に位置情報を付与しました", file.name );
}
}
@@ -676,4 +574,19 @@ private void updateFileLocation( final Location location, FilePathX file ){
}
+ @SuppressWarnings( "deprecation" )
+ public static Object getWiFiNetwork( Context context ){
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE );
+ if( Build.VERSION.SDK_INT >= 21 ){
+ for( Network n : cm.getAllNetworks() ){
+ NetworkInfo info = cm.getNetworkInfo( n );
+ if( info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI ) return n;
+ }
+ }else{
+ for( NetworkInfo info : cm.getAllNetworkInfo() ){
+ if( info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI ) return info;
+ }
+ }
+ return null;
+ }
}
diff --git a/app/src/main/java/jp/juggler/fadownloader/FilePathX.java b/app/src/main/java/jp/juggler/fadownloader/FilePathX.java
new file mode 100644
index 0000000..eb5ffd7
--- /dev/null
+++ b/app/src/main/java/jp/juggler/fadownloader/FilePathX.java
@@ -0,0 +1,193 @@
+package jp.juggler.fadownloader;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.support.v4.provider.DocumentFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class FilePathX{
+
+ static final int DOCUMENT_FILE_VERSION = 21;
+
+ private static Object bsearch( ArrayList