mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-30 08:34:16 +01:00
fixes to data export/import
Main problem, aside from the explanations being stubbed out, is that zip file overwriting doesn't work: once an entry is in the file, it'll be there when the file's overwritten even if that entry is excluded. This happens even if I create the file in memory and then overwrite using the bytes.
This commit is contained in:
parent
d73481ba9b
commit
d3d702ad9e
11 changed files with 519 additions and 141 deletions
|
@ -386,6 +386,7 @@ dependencies {
|
||||||
// implementation("com.hivemq:hivemq-mqtt-client:1.3.0")
|
// implementation("com.hivemq:hivemq-mqtt-client:1.3.0")
|
||||||
|
|
||||||
implementation 'com.google.zxing:core:3.3.+'
|
implementation 'com.google.zxing:core:3.3.+'
|
||||||
|
implementation 'com.jakewharton:process-phoenix:2.1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
task mkImages(type: Exec) {
|
task mkImages(type: Exec) {
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2022 by Eric House (xwords@eehouse.org). All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eehouse.android.xw4;
|
||||||
|
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
// import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
// import android.view.View;
|
||||||
|
// import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
// import android.widget.AdapterView;
|
||||||
|
// import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
// import android.widget.RadioButton;
|
||||||
|
// import android.widget.RadioGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
// import android.widget.TextView;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
// import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
|
import org.eehouse.android.xw4.ZipUtils.SaveWhat;
|
||||||
|
|
||||||
|
public class BackupConfigView extends LinearLayout
|
||||||
|
{
|
||||||
|
private static final String TAG = BackupConfigView.class.getSimpleName();
|
||||||
|
|
||||||
|
private boolean mIsStore;
|
||||||
|
private Uri mLoadFile;
|
||||||
|
private Map<SaveWhat, CheckBox> mCheckBoxes = new HashMap<>();
|
||||||
|
private List<SaveWhat> mShowWhats;
|
||||||
|
|
||||||
|
public BackupConfigView( Context cx, AttributeSet as )
|
||||||
|
{
|
||||||
|
super( cx, as );
|
||||||
|
}
|
||||||
|
|
||||||
|
void init( Uri uri )
|
||||||
|
{
|
||||||
|
mLoadFile = uri;
|
||||||
|
mIsStore = null == uri;
|
||||||
|
if ( null != uri ) {
|
||||||
|
mShowWhats = ZipUtils.getHasWhats( getContext(), uri );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate()
|
||||||
|
{
|
||||||
|
Context context = getContext();
|
||||||
|
LinearLayout list = (LinearLayout)findViewById( R.id.whats_list );
|
||||||
|
for ( SaveWhat what : SaveWhat.values() ) {
|
||||||
|
if ( null == mShowWhats || mShowWhats.contains(what) ) {
|
||||||
|
CheckBox box = (CheckBox)
|
||||||
|
LocUtils.inflate( context, R.layout.invite_checkbox );
|
||||||
|
box.setText( what.toString() );
|
||||||
|
mCheckBoxes.put( what, box );
|
||||||
|
list.addView( box );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPosButtonTxt()
|
||||||
|
{
|
||||||
|
return mIsStore ? "Save" : "Load";
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SaveWhat> getSaveWhat()
|
||||||
|
{
|
||||||
|
List<SaveWhat> result = new ArrayList<>();
|
||||||
|
for ( SaveWhat what : mCheckBoxes.keySet() ) {
|
||||||
|
CheckBox box = mCheckBoxes.get( what );
|
||||||
|
if ( box.isChecked() ) {
|
||||||
|
result.add( what );
|
||||||
|
Log.d( TAG, "getSaveWhat(): added %s", what );
|
||||||
|
} else {
|
||||||
|
Log.d( TAG, "getSaveWhat(): DID NOT add %s", what );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d( TAG, "getSaveWhat() => %s", result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class BackupView {
|
||||||
|
}
|
|
@ -1853,41 +1853,6 @@ public class DBUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean loadDB( Context context, Uri uri )
|
|
||||||
{
|
|
||||||
boolean success = false;
|
|
||||||
try ( InputStream is = context
|
|
||||||
.getContentResolver().openInputStream(uri) ) {
|
|
||||||
String name = DBHelper.getDBName();
|
|
||||||
File gamesDB = context.getDatabasePath( name );
|
|
||||||
FileOutputStream fos = new FileOutputStream( gamesDB );
|
|
||||||
success = copyStream( fos, is );
|
|
||||||
invalGroupsCache();
|
|
||||||
} catch ( Exception ex ) {
|
|
||||||
Log.ex( TAG, ex );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( success ) {
|
|
||||||
PrefsDelegate.loadPrefs( context );
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean saveDB( Context context, Uri uri )
|
|
||||||
{
|
|
||||||
PrefsDelegate.savePrefs( context );
|
|
||||||
boolean success = false;
|
|
||||||
try ( OutputStream os = context.getContentResolver().openOutputStream( uri ) ) {
|
|
||||||
String name = DBHelper.getDBName();
|
|
||||||
File gamesDB = context.getDatabasePath( name );
|
|
||||||
FileInputStream fis = new FileInputStream( gamesDB );
|
|
||||||
success = copyStream( os, fis );
|
|
||||||
} catch ( Exception ex ) {
|
|
||||||
Log.ex( TAG, ex );
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean copyStream( OutputStream fos, InputStream fis )
|
public static boolean copyStream( OutputStream fos, InputStream fis )
|
||||||
{
|
{
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -1904,13 +1869,6 @@ public class DBUtils {
|
||||||
Log.d( TAG, "copyFileStream(): copied %s to %s", fis, fos );
|
Log.d( TAG, "copyFileStream(): copied %s to %s", fis, fos );
|
||||||
} catch( java.io.IOException ioe ) {
|
} catch( java.io.IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
fos.close();
|
|
||||||
fis.close();
|
|
||||||
} catch( java.io.IOException ioe ) {
|
|
||||||
Log.ex( TAG, ioe );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,15 @@ import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
|
|
||||||
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
||||||
import org.eehouse.android.xw4.jni.XwJNI;
|
import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -233,9 +234,9 @@ public class DictUtils {
|
||||||
FileInputStream fis = context.openFileInput( name );
|
FileInputStream fis = context.openFileInput( name );
|
||||||
fis.close();
|
fis.close();
|
||||||
loc = DictLoc.INTERNAL;
|
loc = DictLoc.INTERNAL;
|
||||||
} catch ( java.io.FileNotFoundException fnf ) {
|
} catch ( FileNotFoundException fnf ) {
|
||||||
// Log.ex( fnf );
|
// Log.ex( fnf );
|
||||||
} catch ( java.io.IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,8 +305,10 @@ public class DictUtils {
|
||||||
: new FileOutputStream( getDictFile( context, name, to ) );
|
: new FileOutputStream( getDictFile( context, name, to ) );
|
||||||
|
|
||||||
success = DBUtils.copyStream( fos, fis );
|
success = DBUtils.copyStream( fos, fis );
|
||||||
} catch ( java.io.FileNotFoundException fnfe ) {
|
fos.close();
|
||||||
Log.ex( TAG, fnfe );
|
fis.close();
|
||||||
|
} catch ( IOException ex ) {
|
||||||
|
Log.ex( TAG, ex );
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
} // copyDict
|
} // copyDict
|
||||||
|
@ -368,7 +371,7 @@ public class DictUtils {
|
||||||
|
|
||||||
Assert.assertTrue( -1 == dict.read() );
|
Assert.assertTrue( -1 == dict.read() );
|
||||||
bytes = bas.toByteArray();
|
bytes = bas.toByteArray();
|
||||||
} catch ( java.io.IOException ee ){
|
} catch ( IOException ee ){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,9 +406,9 @@ public class DictUtils {
|
||||||
fis.read( bytes, 0, len );
|
fis.read( bytes, 0, len );
|
||||||
fis.close();
|
fis.close();
|
||||||
Log.i( TAG, "Successfully loaded %s", name );
|
Log.i( TAG, "Successfully loaded %s", name );
|
||||||
} catch ( java.io.FileNotFoundException fnf ) {
|
} catch ( FileNotFoundException fnf ) {
|
||||||
// Log.ex( fnf );
|
// Log.ex( fnf );
|
||||||
} catch ( java.io.IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,9 +521,9 @@ public class DictUtils {
|
||||||
if ( success ) {
|
if ( success ) {
|
||||||
invalDictList();
|
invalDictList();
|
||||||
}
|
}
|
||||||
} catch ( java.io.FileNotFoundException fnf ) {
|
} catch ( FileNotFoundException fnf ) {
|
||||||
Log.ex( TAG, fnf );
|
Log.ex( TAG, fnf );
|
||||||
} catch ( java.io.IOException ioe ) {
|
} catch ( IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
tmpFile.delete();
|
tmpFile.delete();
|
||||||
}
|
}
|
||||||
|
@ -607,7 +610,7 @@ public class DictUtils {
|
||||||
try {
|
try {
|
||||||
AssetManager am = context.getAssets();
|
AssetManager am = context.getAssets();
|
||||||
return am.list("");
|
return am.list("");
|
||||||
} catch( java.io.IOException ioe ) {
|
} catch( IOException ioe ) {
|
||||||
Log.ex( TAG, ioe );
|
Log.ex( TAG, ioe );
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,55 +21,56 @@
|
||||||
package org.eehouse.android.xw4;
|
package org.eehouse.android.xw4;
|
||||||
|
|
||||||
public enum DlgID {
|
public enum DlgID {
|
||||||
NONE
|
NONE,
|
||||||
, CHANGE_GROUP
|
CHANGE_GROUP,
|
||||||
, CONFIRM_CHANGE
|
CONFIRM_CHANGE,
|
||||||
, CONFIRM_CHANGE_PLAY
|
CONFIRM_CHANGE_PLAY,
|
||||||
, CONFIRM_THEN
|
CONFIRM_THEN,
|
||||||
, DIALOG_NOTAGAIN
|
DIALOG_NOTAGAIN,
|
||||||
, DIALOG_OKONLY
|
DIALOG_OKONLY,
|
||||||
, DIALOG_ENABLESMS
|
DIALOG_ENABLESMS,
|
||||||
, DICT_OR_DECLINE
|
DICT_OR_DECLINE,
|
||||||
, DLG_CONNSTAT
|
DLG_CONNSTAT,
|
||||||
, DLG_DELETED
|
DLG_DELETED,
|
||||||
, DLG_INVITE(true)
|
DLG_INVITE(true),
|
||||||
, DLG_OKONLY
|
DLG_OKONLY,
|
||||||
, ENABLE_NFC
|
ENABLE_NFC,
|
||||||
, FORCE_REMOTE
|
FORCE_REMOTE,
|
||||||
, GET_NAME
|
GET_NAME,
|
||||||
, GET_NUMBER
|
GET_NUMBER,
|
||||||
, INVITE_CHOICES_THEN
|
INVITE_CHOICES_THEN,
|
||||||
, MOVE_DICT
|
MOVE_DICT,
|
||||||
, NAME_GAME
|
NAME_GAME,
|
||||||
, NEW_GROUP
|
NEW_GROUP,
|
||||||
, PLAYER_EDIT
|
PLAYER_EDIT,
|
||||||
, ENABLE_SMS
|
ENABLE_SMS,
|
||||||
, QUERY_ENDGAME
|
QUERY_ENDGAME,
|
||||||
, RENAME_GAME
|
RENAME_GAME,
|
||||||
, RENAME_GROUP
|
RENAME_GROUP,
|
||||||
, REVERT_ALL
|
REVERT_ALL,
|
||||||
, REVERT_COLORS
|
REVERT_COLORS,
|
||||||
, SET_DEFAULT
|
SET_DEFAULT,
|
||||||
, SHOW_SUBST
|
SHOW_SUBST,
|
||||||
, WARN_NODICT_GENERIC // the general trying-to-open case
|
WARN_NODICT_GENERIC, // the general trying-to-open case
|
||||||
, WARN_NODICT_INVITED // when responding to invitation
|
WARN_NODICT_INVITED, // when responding to invitation
|
||||||
, WARN_NODICT_SUBST // when a substitution will be possible/suggested
|
WARN_NODICT_SUBST, // when a substitution will be possible/suggested
|
||||||
, DLG_BADWORDS
|
DLG_BADWORDS,
|
||||||
, NOTIFY_BADWORDS
|
NOTIFY_BADWORDS,
|
||||||
, QUERY_MOVE
|
QUERY_MOVE,
|
||||||
, QUERY_TRADE
|
QUERY_TRADE,
|
||||||
, ASK_PASSWORD
|
ASK_PASSWORD,
|
||||||
, DLG_RETRY
|
DLG_RETRY,
|
||||||
, DLG_SCORES(true)
|
DLG_SCORES(true),
|
||||||
, DLG_USEDICT
|
DLG_USEDICT,
|
||||||
, DLG_GETDICT
|
DLG_GETDICT,
|
||||||
, GAMES_LIST_NEWGAME
|
GAMES_LIST_NEWGAME,
|
||||||
, CHANGE_CONN
|
CHANGE_CONN,
|
||||||
, GAMES_LIST_NAME_REMATCH
|
GAMES_LIST_NAME_REMATCH,
|
||||||
, ASK_DUP_PAUSE
|
ASK_DUP_PAUSE,
|
||||||
, CHOOSE_TILES
|
CHOOSE_TILES,
|
||||||
, SHOW_TILES
|
SHOW_TILES,
|
||||||
, RENAME_PLAYER
|
RENAME_PLAYER,
|
||||||
|
BACKUP_LOADSTORE,
|
||||||
;
|
;
|
||||||
|
|
||||||
private boolean m_addToStack;
|
private boolean m_addToStack;
|
||||||
|
|
|
@ -47,15 +47,17 @@ import android.widget.LinearLayout;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.jakewharton.processphoenix.ProcessPhoenix;
|
||||||
|
|
||||||
import org.eehouse.android.xw4.DBUtils.GameChangeType;
|
import org.eehouse.android.xw4.DBUtils.GameChangeType;
|
||||||
import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
|
import org.eehouse.android.xw4.DBUtils.GameGroupInfo;
|
||||||
import static org.eehouse.android.xw4.DBUtils.ROWID_NOTFOUND;
|
|
||||||
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
import org.eehouse.android.xw4.DBUtils.SentInvitesInfo;
|
||||||
import org.eehouse.android.xw4.DlgDelegate.Action;
|
import org.eehouse.android.xw4.DlgDelegate.Action;
|
||||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||||
import org.eehouse.android.xw4.DwnldDelegate.DownloadFinishedListener;
|
import org.eehouse.android.xw4.DwnldDelegate.DownloadFinishedListener;
|
||||||
import org.eehouse.android.xw4.DwnldDelegate.OnGotLcDictListener;
|
import org.eehouse.android.xw4.DwnldDelegate.OnGotLcDictListener;
|
||||||
import org.eehouse.android.xw4.Perms23.Perm;
|
import org.eehouse.android.xw4.Perms23.Perm;
|
||||||
|
import org.eehouse.android.xw4.ZipUtils.SaveWhat;
|
||||||
import org.eehouse.android.xw4.jni.CommonPrefs;
|
import org.eehouse.android.xw4.jni.CommonPrefs;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
|
||||||
|
@ -65,6 +67,7 @@ import org.eehouse.android.xw4.jni.GameSummary;
|
||||||
import org.eehouse.android.xw4.jni.LastMoveInfo;
|
import org.eehouse.android.xw4.jni.LastMoveInfo;
|
||||||
import org.eehouse.android.xw4.jni.XwJNI;
|
import org.eehouse.android.xw4.jni.XwJNI;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
import static org.eehouse.android.xw4.DBUtils.ROWID_NOTFOUND;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -739,6 +742,11 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BACKUP_LOADSTORE:
|
||||||
|
Uri uri = 0 == params.length ? null : Uri.parse((String)params[0]);
|
||||||
|
dialog = mkLoadStoreDlg( uri );
|
||||||
|
break;
|
||||||
|
|
||||||
case NEW_GROUP: {
|
case NEW_GROUP: {
|
||||||
final Renamer namer = buildRenamer( "", R.string.newgroup_label );
|
final Renamer namer = buildRenamer( "", R.string.newgroup_label );
|
||||||
lstnr = new OnClickListener() {
|
lstnr = new OnClickListener() {
|
||||||
|
@ -1544,10 +1552,44 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLoadOrStore( boolean isStore )
|
private Dialog mkLoadStoreDlg( final Uri uri )
|
||||||
{
|
{
|
||||||
|
Log.d( TAG, "mkLoadStoreDlg(%s)", uri );
|
||||||
|
final BackupConfigView view = (BackupConfigView)
|
||||||
|
LocUtils.inflate( m_activity, R.layout.backup_config_view );
|
||||||
|
view.init( uri );
|
||||||
|
|
||||||
|
AlertDialog.Builder ab = makeAlertBuilder()
|
||||||
|
.setView( view )
|
||||||
|
.setPositiveButton( view.getPosButtonTxt(), new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick( DialogInterface dlg, int item ) {
|
||||||
|
if ( null == uri ) { // store case
|
||||||
|
startFileChooser( view.getSaveWhat() );
|
||||||
|
} else {
|
||||||
|
List<ZipUtils.SaveWhat> what = view.getSaveWhat();
|
||||||
|
if ( ZipUtils.load( m_activity, uri, what ) ) {
|
||||||
|
ProcessPhoenix.triggerRebirth( m_activity );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.setNegativeButton( android.R.string.cancel, null )
|
||||||
|
;
|
||||||
|
return ab.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is in liu of passing through the startActivityForResult call,
|
||||||
|
// which apparently isn't supported.
|
||||||
|
private List<ZipUtils.SaveWhat> mSaveWhat;
|
||||||
|
|
||||||
|
private void startFileChooser( List<SaveWhat> what )
|
||||||
|
{
|
||||||
|
mSaveWhat = what; // will be null in load case
|
||||||
|
|
||||||
String intentAction = null;
|
String intentAction = null;
|
||||||
RequestCode rq = null;
|
RequestCode rq = null;
|
||||||
|
boolean isStore = null != what;
|
||||||
if ( isStore ) {
|
if ( isStore ) {
|
||||||
intentAction = Intent.ACTION_CREATE_DOCUMENT;
|
intentAction = Intent.ACTION_CREATE_DOCUMENT;
|
||||||
rq = RequestCode.STORE_DATA_FILE;
|
rq = RequestCode.STORE_DATA_FILE;
|
||||||
|
@ -1557,30 +1599,13 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
}
|
}
|
||||||
Intent intent = new Intent( intentAction );
|
Intent intent = new Intent( intentAction );
|
||||||
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
||||||
intent.setType( "application/octet-stream" );
|
intent.setType( ZipUtils.getMimeType() );
|
||||||
if ( isStore ) {
|
if ( isStore ) {
|
||||||
intent.putExtra( Intent.EXTRA_TITLE, DBHelper.getDBName() );
|
intent.putExtra( Intent.EXTRA_TITLE, DBHelper.getDBName() );
|
||||||
}
|
}
|
||||||
startActivityForResult( intent, rq );
|
startActivityForResult( intent, rq );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleLoadOrStoreResult( Uri uri, boolean isStore )
|
|
||||||
{
|
|
||||||
if ( isStore ) {
|
|
||||||
boolean saved = DBUtils.saveDB( m_activity, uri );
|
|
||||||
int msgID = saved ? R.string.db_store_done
|
|
||||||
: R.string.db_store_failed;
|
|
||||||
showToast( msgID );
|
|
||||||
} else {
|
|
||||||
if ( DBUtils.loadDB( m_activity, uri ) ) {
|
|
||||||
storeGroupPositions( null );
|
|
||||||
mkListAdapter();
|
|
||||||
// We really want to exit the app!!! PENDING
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onNegButton( Action action, Object[] params )
|
public boolean onNegButton( Action action, Object[] params )
|
||||||
{
|
{
|
||||||
|
@ -1625,9 +1650,23 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
case STORE_DATA_FILE:
|
case STORE_DATA_FILE:
|
||||||
case LOAD_DATA_FILE:
|
case LOAD_DATA_FILE:
|
||||||
if ( Activity.RESULT_OK == resultCode && data != null ) {
|
if ( Activity.RESULT_OK == resultCode && data != null ) {
|
||||||
boolean isStore = RequestCode.STORE_DATA_FILE == requestCode;
|
|
||||||
Uri uri = data.getData();
|
Uri uri = data.getData();
|
||||||
handleLoadOrStoreResult( uri, isStore );
|
boolean isStore = RequestCode.STORE_DATA_FILE == requestCode;
|
||||||
|
if ( isStore ) {
|
||||||
|
boolean saved =
|
||||||
|
ZipUtils.save( m_activity, uri, mSaveWhat );
|
||||||
|
int msgID = saved ? R.string.db_store_done
|
||||||
|
: R.string.db_store_failed;
|
||||||
|
showToast( msgID );
|
||||||
|
} else {
|
||||||
|
final String uriStr = uri.toString();
|
||||||
|
post( new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
showDialogFragment( DlgID.BACKUP_LOADSTORE, uriStr );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1843,9 +1882,18 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
Utils.emailAuthor( m_activity );
|
Utils.emailAuthor( m_activity );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.games_menu_loaddb:
|
// Load and store both use the same dialog, and both use the
|
||||||
|
// ContentResolver/startActivityForResult grossness, but in
|
||||||
|
// different orders. Store needs to know *what* to store before
|
||||||
|
// asking where, but load needs to know what's being loaded before
|
||||||
|
// asking what subset of that the user wants to use. So we start
|
||||||
|
// with the choose-what alert in the store case, and with the
|
||||||
|
// choose-where (OS) grossness in the load case
|
||||||
case R.id.games_menu_storedb:
|
case R.id.games_menu_storedb:
|
||||||
startLoadOrStore( R.id.games_menu_storedb == itemID );
|
showDialogFragment( DlgID.BACKUP_LOADSTORE );
|
||||||
|
break;
|
||||||
|
case R.id.games_menu_loaddb:
|
||||||
|
startFileChooser( null );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.games_menu_writegit:
|
case R.id.games_menu_writegit:
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.eehouse.android.xw4.jni.CommonPrefs;
|
||||||
import org.eehouse.android.xw4.loc.LocUtils;
|
import org.eehouse.android.xw4.loc.LocUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -367,21 +368,20 @@ public class PrefsDelegate extends DelegateBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void savePrefs( Context context )
|
static Serializable getPrefs( Context context )
|
||||||
{
|
{
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( context );
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( context );
|
||||||
Map<String, ?> all = prefs.getAll();
|
Map<String, ?> all = prefs.getAll();
|
||||||
HashMap<String, Object> copy = new HashMap<>();
|
HashMap<String, Object> result = new HashMap<>();
|
||||||
for ( String key : all.keySet() ) {
|
for ( String key : all.keySet() ) {
|
||||||
copy.put( key, all.get(key) );
|
result.put( key, all.get(key) );
|
||||||
}
|
}
|
||||||
DBUtils.setSerializableFor( context, PREFS_KEY, copy );
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadPrefs( Context context ) {
|
static void loadPrefs( Context context, Serializable obj ) {
|
||||||
HashMap<String, Object> map = (HashMap<String, Object>)DBUtils
|
if ( null != obj ) {
|
||||||
.getSerializableFor( context, PREFS_KEY );
|
HashMap<String, Object> map = (HashMap<String, Object>)obj;
|
||||||
if ( null != map ) {
|
|
||||||
SharedPreferences.Editor editor =
|
SharedPreferences.Editor editor =
|
||||||
PreferenceManager.getDefaultSharedPreferences( context )
|
PreferenceManager.getDefaultSharedPreferences( context )
|
||||||
.edit();
|
.edit();
|
||||||
|
|
|
@ -788,29 +788,35 @@ public class Utils {
|
||||||
return Base64.decode( in, Base64.NO_WRAP );
|
return Base64.decode( in, Base64.NO_WRAP );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object string64ToSerializable( String str64 )
|
public static Serializable bytesToSerializable( byte[] bytes )
|
||||||
{
|
{
|
||||||
Object result = null;
|
Serializable result = null;
|
||||||
byte[] bytes = base64Decode( str64 );
|
|
||||||
try {
|
try {
|
||||||
ObjectInputStream ois =
|
ObjectInputStream ois =
|
||||||
new ObjectInputStream( new ByteArrayInputStream(bytes) );
|
new ObjectInputStream( new ByteArrayInputStream(bytes) );
|
||||||
result = ois.readObject();
|
result = (Serializable)ois.readObject();
|
||||||
} catch ( Exception ex ) {
|
} catch ( Exception ex ) {
|
||||||
Log.d( TAG, "%s", ex.getMessage() );
|
Log.d( TAG, "%s", ex.getMessage() );
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String serializableToString64( Serializable obj )
|
public static Object string64ToSerializable( String str64 )
|
||||||
{
|
{
|
||||||
String result = null;
|
byte[] bytes = base64Decode( str64 );
|
||||||
|
Serializable result = bytesToSerializable(bytes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] serializableToBytes( Serializable obj )
|
||||||
|
{
|
||||||
|
byte[] result = null;
|
||||||
ByteArrayOutputStream bas = new ByteArrayOutputStream();
|
ByteArrayOutputStream bas = new ByteArrayOutputStream();
|
||||||
try {
|
try {
|
||||||
ObjectOutputStream out = new ObjectOutputStream( bas );
|
ObjectOutputStream out = new ObjectOutputStream( bas );
|
||||||
out.writeObject( obj );
|
out.writeObject( obj );
|
||||||
out.flush();
|
out.flush();
|
||||||
result = base64Encode( bas.toByteArray() );
|
result = bas.toByteArray();
|
||||||
} catch ( Exception ex ) {
|
} catch ( Exception ex ) {
|
||||||
Log.ex( TAG, ex );
|
Log.ex( TAG, ex );
|
||||||
Assert.failDbg();
|
Assert.failDbg();
|
||||||
|
@ -818,6 +824,13 @@ public class Utils {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String serializableToString64( Serializable obj )
|
||||||
|
{
|
||||||
|
byte[] asBytes = serializableToBytes( obj );
|
||||||
|
String result = base64Encode( asBytes );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static void testSerialization( Serializable obj )
|
public static void testSerialization( Serializable obj )
|
||||||
{
|
{
|
||||||
if ( false && BuildConfig.DEBUG ) {
|
if ( false && BuildConfig.DEBUG ) {
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2022 by Eric House (xwords@eehouse.org). All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eehouse.android.xw4;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class ZipUtils {
|
||||||
|
private static final String TAG = ZipUtils.class.getSimpleName();
|
||||||
|
|
||||||
|
public static enum SaveWhat { // COLORS,
|
||||||
|
SETTINGS,
|
||||||
|
GAMES,
|
||||||
|
;
|
||||||
|
|
||||||
|
String entryName() { return toString(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static String getMimeType() {
|
||||||
|
return "application/x-zip";
|
||||||
|
// return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface EntryIter {
|
||||||
|
boolean withEntry( ZipInputStream zis, SaveWhat what ) throws FileNotFoundException, IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SaveWhat> getHasWhats( Context context, Uri uri )
|
||||||
|
{
|
||||||
|
final List<SaveWhat> result = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
iterate( context, uri, new EntryIter() {
|
||||||
|
@Override
|
||||||
|
public boolean withEntry( ZipInputStream zis, SaveWhat what ) {
|
||||||
|
result.add( what );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} catch ( IOException ioe ) {
|
||||||
|
Log.ex( TAG, ioe );
|
||||||
|
}
|
||||||
|
Log.d( TAG, "getHasWhats() => %s", result );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean load( Context context, Uri uri,
|
||||||
|
final List<SaveWhat> whats )
|
||||||
|
{
|
||||||
|
Log.d( TAG, "load(%s)", whats );
|
||||||
|
boolean result = false;
|
||||||
|
try {
|
||||||
|
result = iterate( context, uri, new EntryIter() {
|
||||||
|
@Override
|
||||||
|
public boolean withEntry( ZipInputStream zis, SaveWhat what )
|
||||||
|
throws FileNotFoundException, IOException {
|
||||||
|
boolean success = false;
|
||||||
|
if ( whats.contains( what ) ) {
|
||||||
|
switch ( what ) {
|
||||||
|
case SETTINGS:
|
||||||
|
success = loadSettings( context, zis );
|
||||||
|
break;
|
||||||
|
case GAMES:
|
||||||
|
success = loadGames( context, zis );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Assert.failDbg();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} catch ( Exception ex ) {
|
||||||
|
Log.ex( TAG, ex );
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean iterate( Context context, Uri uri, EntryIter iter )
|
||||||
|
throws IOException, FileNotFoundException
|
||||||
|
{
|
||||||
|
boolean success = true;
|
||||||
|
try ( InputStream is = context
|
||||||
|
.getContentResolver().openInputStream( uri ) ) {
|
||||||
|
ZipInputStream zis = new ZipInputStream( is );
|
||||||
|
while ( success ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if ( null == ze ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String name = ze.getName();
|
||||||
|
Log.d( TAG, "next entry name: %s", name );
|
||||||
|
SaveWhat what = SaveWhat.valueOf( name );
|
||||||
|
success = iter.withEntry( zis, what );
|
||||||
|
}
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean save( Context context, Uri uri,
|
||||||
|
List<SaveWhat> whats )
|
||||||
|
{
|
||||||
|
Log.d( TAG, "save(%s)", whats );
|
||||||
|
boolean success = false;
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
// resolver.delete( uri, null, null ); // nuke the file if exists
|
||||||
|
try ( OutputStream os = resolver.openOutputStream( uri ) ) {
|
||||||
|
ZipOutputStream zos = new ZipOutputStream( os ) ;
|
||||||
|
|
||||||
|
for ( SaveWhat what : whats ) {
|
||||||
|
zos.putNextEntry( new ZipEntry( what.entryName() ) );
|
||||||
|
switch ( what ) {
|
||||||
|
// case SAVE_COLORS:
|
||||||
|
// success = saveColors( zos, ze );
|
||||||
|
// break;
|
||||||
|
case SETTINGS:
|
||||||
|
success = saveSettings( context, zos );
|
||||||
|
break;
|
||||||
|
case GAMES:
|
||||||
|
success = saveGames( context, zos );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Assert.failDbg();
|
||||||
|
}
|
||||||
|
if ( success ) {
|
||||||
|
zos.closeEntry();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zos.close();
|
||||||
|
os.close();
|
||||||
|
} catch ( Exception ex ) {
|
||||||
|
Log.ex( TAG, ex );
|
||||||
|
}
|
||||||
|
Log.d( TAG, "save(%s) DONE", whats );
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean saveGames( Context context, ZipOutputStream zos )
|
||||||
|
throws FileNotFoundException, IOException
|
||||||
|
{
|
||||||
|
String name = DBHelper.getDBName();
|
||||||
|
File gamesDB = context.getDatabasePath( name );
|
||||||
|
FileInputStream fis = new FileInputStream( gamesDB );
|
||||||
|
boolean success = DBUtils.copyStream( zos, fis );
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean loadGames( Context context, ZipInputStream zis )
|
||||||
|
throws FileNotFoundException, IOException
|
||||||
|
{
|
||||||
|
String name = DBHelper.getDBName();
|
||||||
|
File gamesDB = context.getDatabasePath( name );
|
||||||
|
FileOutputStream fos = new FileOutputStream( gamesDB );
|
||||||
|
boolean success = DBUtils.copyStream( fos, zis );
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean saveSettings( Context context, ZipOutputStream zos )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
Serializable map = PrefsDelegate.getPrefs( context );
|
||||||
|
byte[] asBytes = Utils.serializableToBytes( map );
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream( asBytes );
|
||||||
|
boolean success = DBUtils.copyStream( zos, bis );
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean loadSettings( Context context, ZipInputStream zis )
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
boolean success = DBUtils.copyStream( bos, zis );
|
||||||
|
if ( success ) {
|
||||||
|
Serializable map = Utils.bytesToSerializable( bos.toByteArray() );
|
||||||
|
PrefsDelegate.loadPrefs( context, map );
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<org.eehouse.android.xw4.BackupConfigView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="8dp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/explanation"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="replace me"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Checkboxes get added here -->
|
||||||
|
<LinearLayout android:id="@+id/whats_list"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</org.eehouse.android.xw4.BackupConfigView>
|
Loading…
Add table
Reference in a new issue