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.google.zxing:core:3.3.+'
|
||||
implementation 'com.jakewharton:process-phoenix:2.1.2'
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
boolean success = false;
|
||||
|
@ -1904,13 +1869,6 @@ public class DBUtils {
|
|||
Log.d( TAG, "copyFileStream(): copied %s to %s", fis, fos );
|
||||
} catch( java.io.IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
} finally {
|
||||
try {
|
||||
fos.close();
|
||||
fis.close();
|
||||
} catch( java.io.IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -24,14 +24,15 @@ import android.content.Context;
|
|||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
|
||||
|
||||
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
|
||||
import org.eehouse.android.xw4.jni.XwJNI;
|
||||
|
||||
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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -233,9 +234,9 @@ public class DictUtils {
|
|||
FileInputStream fis = context.openFileInput( name );
|
||||
fis.close();
|
||||
loc = DictLoc.INTERNAL;
|
||||
} catch ( java.io.FileNotFoundException fnf ) {
|
||||
} catch ( FileNotFoundException fnf ) {
|
||||
// Log.ex( fnf );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
} catch ( IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
}
|
||||
}
|
||||
|
@ -304,8 +305,10 @@ public class DictUtils {
|
|||
: new FileOutputStream( getDictFile( context, name, to ) );
|
||||
|
||||
success = DBUtils.copyStream( fos, fis );
|
||||
} catch ( java.io.FileNotFoundException fnfe ) {
|
||||
Log.ex( TAG, fnfe );
|
||||
fos.close();
|
||||
fis.close();
|
||||
} catch ( IOException ex ) {
|
||||
Log.ex( TAG, ex );
|
||||
}
|
||||
return success;
|
||||
} // copyDict
|
||||
|
@ -368,7 +371,7 @@ public class DictUtils {
|
|||
|
||||
Assert.assertTrue( -1 == dict.read() );
|
||||
bytes = bas.toByteArray();
|
||||
} catch ( java.io.IOException ee ){
|
||||
} catch ( IOException ee ){
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,9 +406,9 @@ public class DictUtils {
|
|||
fis.read( bytes, 0, len );
|
||||
fis.close();
|
||||
Log.i( TAG, "Successfully loaded %s", name );
|
||||
} catch ( java.io.FileNotFoundException fnf ) {
|
||||
} catch ( FileNotFoundException fnf ) {
|
||||
// Log.ex( fnf );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
} catch ( IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
}
|
||||
}
|
||||
|
@ -518,9 +521,9 @@ public class DictUtils {
|
|||
if ( success ) {
|
||||
invalDictList();
|
||||
}
|
||||
} catch ( java.io.FileNotFoundException fnf ) {
|
||||
} catch ( FileNotFoundException fnf ) {
|
||||
Log.ex( TAG, fnf );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
} catch ( IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
tmpFile.delete();
|
||||
}
|
||||
|
@ -607,7 +610,7 @@ public class DictUtils {
|
|||
try {
|
||||
AssetManager am = context.getAssets();
|
||||
return am.list("");
|
||||
} catch( java.io.IOException ioe ) {
|
||||
} catch( IOException ioe ) {
|
||||
Log.ex( TAG, ioe );
|
||||
return new String[0];
|
||||
}
|
||||
|
|
|
@ -21,55 +21,56 @@
|
|||
package org.eehouse.android.xw4;
|
||||
|
||||
public enum DlgID {
|
||||
NONE
|
||||
, CHANGE_GROUP
|
||||
, CONFIRM_CHANGE
|
||||
, CONFIRM_CHANGE_PLAY
|
||||
, CONFIRM_THEN
|
||||
, DIALOG_NOTAGAIN
|
||||
, DIALOG_OKONLY
|
||||
, DIALOG_ENABLESMS
|
||||
, DICT_OR_DECLINE
|
||||
, DLG_CONNSTAT
|
||||
, DLG_DELETED
|
||||
, DLG_INVITE(true)
|
||||
, DLG_OKONLY
|
||||
, ENABLE_NFC
|
||||
, FORCE_REMOTE
|
||||
, GET_NAME
|
||||
, GET_NUMBER
|
||||
, INVITE_CHOICES_THEN
|
||||
, MOVE_DICT
|
||||
, NAME_GAME
|
||||
, NEW_GROUP
|
||||
, PLAYER_EDIT
|
||||
, ENABLE_SMS
|
||||
, QUERY_ENDGAME
|
||||
, RENAME_GAME
|
||||
, RENAME_GROUP
|
||||
, REVERT_ALL
|
||||
, REVERT_COLORS
|
||||
, SET_DEFAULT
|
||||
, SHOW_SUBST
|
||||
, WARN_NODICT_GENERIC // the general trying-to-open case
|
||||
, WARN_NODICT_INVITED // when responding to invitation
|
||||
, WARN_NODICT_SUBST // when a substitution will be possible/suggested
|
||||
, DLG_BADWORDS
|
||||
, NOTIFY_BADWORDS
|
||||
, QUERY_MOVE
|
||||
, QUERY_TRADE
|
||||
, ASK_PASSWORD
|
||||
, DLG_RETRY
|
||||
, DLG_SCORES(true)
|
||||
, DLG_USEDICT
|
||||
, DLG_GETDICT
|
||||
, GAMES_LIST_NEWGAME
|
||||
, CHANGE_CONN
|
||||
, GAMES_LIST_NAME_REMATCH
|
||||
, ASK_DUP_PAUSE
|
||||
, CHOOSE_TILES
|
||||
, SHOW_TILES
|
||||
, RENAME_PLAYER
|
||||
NONE,
|
||||
CHANGE_GROUP,
|
||||
CONFIRM_CHANGE,
|
||||
CONFIRM_CHANGE_PLAY,
|
||||
CONFIRM_THEN,
|
||||
DIALOG_NOTAGAIN,
|
||||
DIALOG_OKONLY,
|
||||
DIALOG_ENABLESMS,
|
||||
DICT_OR_DECLINE,
|
||||
DLG_CONNSTAT,
|
||||
DLG_DELETED,
|
||||
DLG_INVITE(true),
|
||||
DLG_OKONLY,
|
||||
ENABLE_NFC,
|
||||
FORCE_REMOTE,
|
||||
GET_NAME,
|
||||
GET_NUMBER,
|
||||
INVITE_CHOICES_THEN,
|
||||
MOVE_DICT,
|
||||
NAME_GAME,
|
||||
NEW_GROUP,
|
||||
PLAYER_EDIT,
|
||||
ENABLE_SMS,
|
||||
QUERY_ENDGAME,
|
||||
RENAME_GAME,
|
||||
RENAME_GROUP,
|
||||
REVERT_ALL,
|
||||
REVERT_COLORS,
|
||||
SET_DEFAULT,
|
||||
SHOW_SUBST,
|
||||
WARN_NODICT_GENERIC, // the general trying-to-open case
|
||||
WARN_NODICT_INVITED, // when responding to invitation
|
||||
WARN_NODICT_SUBST, // when a substitution will be possible/suggested
|
||||
DLG_BADWORDS,
|
||||
NOTIFY_BADWORDS,
|
||||
QUERY_MOVE,
|
||||
QUERY_TRADE,
|
||||
ASK_PASSWORD,
|
||||
DLG_RETRY,
|
||||
DLG_SCORES(true),
|
||||
DLG_USEDICT,
|
||||
DLG_GETDICT,
|
||||
GAMES_LIST_NEWGAME,
|
||||
CHANGE_CONN,
|
||||
GAMES_LIST_NAME_REMATCH,
|
||||
ASK_DUP_PAUSE,
|
||||
CHOOSE_TILES,
|
||||
SHOW_TILES,
|
||||
RENAME_PLAYER,
|
||||
BACKUP_LOADSTORE,
|
||||
;
|
||||
|
||||
private boolean m_addToStack;
|
||||
|
|
|
@ -47,15 +47,17 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix;
|
||||
|
||||
import org.eehouse.android.xw4.DBUtils.GameChangeType;
|
||||
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.DlgDelegate.Action;
|
||||
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
|
||||
import org.eehouse.android.xw4.DwnldDelegate.DownloadFinishedListener;
|
||||
import org.eehouse.android.xw4.DwnldDelegate.OnGotLcDictListener;
|
||||
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.CommsAddrRec.CommsConnType;
|
||||
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.XwJNI;
|
||||
import org.eehouse.android.xw4.loc.LocUtils;
|
||||
import static org.eehouse.android.xw4.DBUtils.ROWID_NOTFOUND;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
@ -739,6 +742,11 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
}
|
||||
break;
|
||||
|
||||
case BACKUP_LOADSTORE:
|
||||
Uri uri = 0 == params.length ? null : Uri.parse((String)params[0]);
|
||||
dialog = mkLoadStoreDlg( uri );
|
||||
break;
|
||||
|
||||
case NEW_GROUP: {
|
||||
final Renamer namer = buildRenamer( "", R.string.newgroup_label );
|
||||
lstnr = new OnClickListener() {
|
||||
|
@ -1544,10 +1552,44 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
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;
|
||||
RequestCode rq = null;
|
||||
boolean isStore = null != what;
|
||||
if ( isStore ) {
|
||||
intentAction = Intent.ACTION_CREATE_DOCUMENT;
|
||||
rq = RequestCode.STORE_DATA_FILE;
|
||||
|
@ -1557,30 +1599,13 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
}
|
||||
Intent intent = new Intent( intentAction );
|
||||
intent.addCategory( Intent.CATEGORY_OPENABLE );
|
||||
intent.setType( "application/octet-stream" );
|
||||
intent.setType( ZipUtils.getMimeType() );
|
||||
if ( isStore ) {
|
||||
intent.putExtra( Intent.EXTRA_TITLE, DBHelper.getDBName() );
|
||||
}
|
||||
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
|
||||
public boolean onNegButton( Action action, Object[] params )
|
||||
{
|
||||
|
@ -1625,9 +1650,23 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
case STORE_DATA_FILE:
|
||||
case LOAD_DATA_FILE:
|
||||
if ( Activity.RESULT_OK == resultCode && data != null ) {
|
||||
boolean isStore = RequestCode.STORE_DATA_FILE == requestCode;
|
||||
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;
|
||||
}
|
||||
|
@ -1843,9 +1882,18 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
Utils.emailAuthor( m_activity );
|
||||
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:
|
||||
startLoadOrStore( R.id.games_menu_storedb == itemID );
|
||||
showDialogFragment( DlgID.BACKUP_LOADSTORE );
|
||||
break;
|
||||
case R.id.games_menu_loaddb:
|
||||
startFileChooser( null );
|
||||
break;
|
||||
|
||||
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 java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
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 );
|
||||
Map<String, ?> all = prefs.getAll();
|
||||
HashMap<String, Object> copy = new HashMap<>();
|
||||
HashMap<String, Object> result = new HashMap<>();
|
||||
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 ) {
|
||||
HashMap<String, Object> map = (HashMap<String, Object>)DBUtils
|
||||
.getSerializableFor( context, PREFS_KEY );
|
||||
if ( null != map ) {
|
||||
static void loadPrefs( Context context, Serializable obj ) {
|
||||
if ( null != obj ) {
|
||||
HashMap<String, Object> map = (HashMap<String, Object>)obj;
|
||||
SharedPreferences.Editor editor =
|
||||
PreferenceManager.getDefaultSharedPreferences( context )
|
||||
.edit();
|
||||
|
|
|
@ -788,29 +788,35 @@ public class Utils {
|
|||
return Base64.decode( in, Base64.NO_WRAP );
|
||||
}
|
||||
|
||||
public static Object string64ToSerializable( String str64 )
|
||||
public static Serializable bytesToSerializable( byte[] bytes )
|
||||
{
|
||||
Object result = null;
|
||||
byte[] bytes = base64Decode( str64 );
|
||||
Serializable result = null;
|
||||
try {
|
||||
ObjectInputStream ois =
|
||||
new ObjectInputStream( new ByteArrayInputStream(bytes) );
|
||||
result = ois.readObject();
|
||||
result = (Serializable)ois.readObject();
|
||||
} catch ( Exception ex ) {
|
||||
Log.d( TAG, "%s", ex.getMessage() );
|
||||
}
|
||||
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();
|
||||
try {
|
||||
ObjectOutputStream out = new ObjectOutputStream( bas );
|
||||
out.writeObject( obj );
|
||||
out.flush();
|
||||
result = base64Encode( bas.toByteArray() );
|
||||
result = bas.toByteArray();
|
||||
} catch ( Exception ex ) {
|
||||
Log.ex( TAG, ex );
|
||||
Assert.failDbg();
|
||||
|
@ -818,6 +824,13 @@ public class Utils {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static String serializableToString64( Serializable obj )
|
||||
{
|
||||
byte[] asBytes = serializableToBytes( obj );
|
||||
String result = base64Encode( asBytes );
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void testSerialization( Serializable obj )
|
||||
{
|
||||
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