From 8c4b4f85aaf7d948e38ae37a20a37ae5fe9b1219 Mon Sep 17 00:00:00 2001 From: stom79 Date: Fri, 25 Jan 2019 11:41:39 +0100 Subject: [PATCH] export / import data --- app/build.gradle | 2 + .../mastodon/activities/BaseMainActivity.java | 123 +++++++++++++++--- .../mastodon/activities/TootActivity.java | 2 +- .../fr/gouv/etalab/mastodon/client/API.java | 8 +- .../mastodon/client/Entities/Emojis.java | 53 +++++--- app/src/main/res/menu/main.xml | 8 ++ app/src/main/res/values/strings.xml | 8 ++ 7 files changed, 166 insertions(+), 38 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d84ea6522..22f4d2ca1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -90,7 +90,9 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation 'com.google.android.exoplayer:exoplayer:2.9.3' implementation 'com.github.stom79:android-upload-service:3.4.2-Mastalab' + implementation 'org.apache.poi:poi:3.16' implementation 'com.github.mabbas007:TagsEditText:1.0.5' implementation 'com.jaredrummler:material-spinner:1.3.1' + implementation 'com.github.stom79:SQLite2XL:1.0.5' playstoreImplementation "io.github.kobakei:ratethisapp:$ratethisappLibraryVersion" } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java index fe8bc57d1..c8fa1a46c 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java @@ -25,6 +25,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; +import android.graphics.BitmapFactory; import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; @@ -54,6 +55,7 @@ import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.InputFilter; import android.text.TextWatcher; +import android.util.Log; import android.util.Patterns; import android.util.SparseArray; import android.view.Gravity; @@ -77,15 +79,23 @@ import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; +import com.ajts.androidmads.library.ExcelToSQLite; +import com.ajts.androidmads.library.SQLiteToExcel; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.File; import java.lang.reflect.Method; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; +import java.util.Random; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -123,6 +133,7 @@ import fr.gouv.etalab.mastodon.fragments.DisplayMutedInstanceFragment; import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayPeertubeNotificationsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayStatusFragment; +import fr.gouv.etalab.mastodon.fragments.SettingsFragment; import fr.gouv.etalab.mastodon.fragments.SettingsPeertubeFragment; import fr.gouv.etalab.mastodon.fragments.TabLayoutScheduleFragment; import fr.gouv.etalab.mastodon.fragments.TabLayoutSettingsFragment; @@ -166,6 +177,7 @@ import static fr.gouv.etalab.mastodon.helper.Helper.changeUser; import static fr.gouv.etalab.mastodon.helper.Helper.menuAccounts; import static fr.gouv.etalab.mastodon.helper.Helper.unCheckAllMenuItems; import static fr.gouv.etalab.mastodon.helper.Helper.updateHeaderAccountInfo; +import static fr.gouv.etalab.mastodon.sqlite.Sqlite.DB_NAME; public abstract class BaseMainActivity extends BaseActivity @@ -211,6 +223,7 @@ public abstract class BaseMainActivity extends BaseActivity private FloatingActionButton federatedTimelines; public static UpdateAccountInfoAsyncTask.SOCIAL social; SparseArray registeredFragments = new SparseArray<>(); + private final int PICK_IMPORT = 5556; @Override protected void onCreate(Bundle savedInstanceState) { @@ -222,7 +235,7 @@ public abstract class BaseMainActivity extends BaseActivity userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(getApplicationContext())); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), DB_NAME, null, Sqlite.DB_VERSION).open(); boolean displayFollowInstance = sharedpreferences.getBoolean(Helper.SET_DISPLAY_FOLLOW_INSTANCE, true); Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(token); if( account == null){ @@ -1340,6 +1353,61 @@ public abstract class BaseMainActivity extends BaseActivity startService(backupIntent); } return true; + + case R.id.action_import_data: + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (ContextCompat.checkSelfPermission(BaseMainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(BaseMainActivity.this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + TootActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); + return true; + } + } + intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + intent.setType("application/vnd.ms-excel"); + String[] mimetypes = {"application/vnd.ms-excel"}; + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes); + startActivityForResult(intent, PICK_IMPORT); + }else { + intent.setType("application/vnd.ms-excel"); + Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + Intent chooserIntent = Intent.createChooser(intent, getString(R.string.toot_select_import)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {pickIntent}); + startActivityForResult(chooserIntent, PICK_IMPORT); + } + return true; + case R.id.action_export_data: + SQLiteToExcel sqliteToExcel = new SQLiteToExcel(BaseMainActivity.this, DB_NAME); + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + final String fileName = "Mastalab_export_"+timeStamp+".xls"; + sqliteToExcel.exportAllTables(fileName, new SQLiteToExcel.ExportListener() { + @Override + public void onStart() { + + } + @Override + public void onCompleted(String filePath) { + final Intent intent = new Intent(); + Random r = new Random(); + final int notificationIdTmp = r.nextInt(10000); + File file = new File(filePath); + intent.setAction(android.content.Intent.ACTION_VIEW); + Uri uri = Uri.fromFile(file); + intent.setDataAndType(uri, "application/vnd.ms-excel"); + Helper.notify_user(getApplicationContext(), intent, notificationIdTmp, BitmapFactory.decodeResource(getResources(), + R.mipmap.ic_launcher), Helper.NotifType.STORE, getString(R.string.save_over), getString(R.string.download_from, fileName)); + Toasty.success(getApplicationContext(), getString(R.string.toast_saved),Toast.LENGTH_LONG).show(); + } + @Override + public void onError(Exception e) { + Toasty.error(getApplicationContext(), getString(R.string.data_export_error_simple),Toast.LENGTH_LONG).show(); + } + }); + return true; default: return true; } @@ -1730,7 +1798,7 @@ public abstract class BaseMainActivity extends BaseActivity bundle.putString("instanceType","PEERTUBE"); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), DB_NAME, null, Sqlite.DB_VERSION).open(); Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(token); bundle.putString("targetedid",account.getUsername()); bundle.putBoolean("ownvideos", true); @@ -2066,7 +2134,7 @@ public abstract class BaseMainActivity extends BaseActivity bundle.putString("instanceType","PEERTUBE"); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), DB_NAME, null, Sqlite.DB_VERSION).open(); Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(token); bundle.putString("targetedid",account.getUsername()); bundle.putBoolean("ownvideos", true); @@ -2189,7 +2257,7 @@ public abstract class BaseMainActivity extends BaseActivity startActivity(myIntent); finish(); }else { - SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, DB_NAME, null, Sqlite.DB_VERSION).open(); Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(userId); updateHeaderAccountInfo(activity, account, headerLayout); } @@ -2199,12 +2267,37 @@ public abstract class BaseMainActivity extends BaseActivity protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - //noinspection StatementWithEmptyBody - if (requestCode == ERROR_DIALOG_REQUEST_CODE) { - // Adding a fragment via GooglePlayServicesUtil.showErrorDialogFragment - // before the instance state is restored throws an error. So instead, - // set a flag here, which will cause the fragment to delay until - // onPostResume. + if (requestCode == PICK_IMPORT && resultCode == Activity.RESULT_OK) { + if (data == null || data.getData() == null) { + Toasty.error(getApplicationContext(),getString(R.string.toot_select_file_error),Toast.LENGTH_LONG).show(); + return; + } + ExcelToSQLite excelToSQLite = new ExcelToSQLite(getApplicationContext(), DB_NAME, true); + String filename = SettingsFragment.getPath(getApplicationContext(), data.getData()); + assert filename != null; + excelToSQLite.importFromFile(filename, new ExcelToSQLite.ImportListener() { + @Override + public void onStart() { + Toasty.success(getApplicationContext(),getString(R.string.data_import_start),Toast.LENGTH_LONG).show(); + } + + @Override + public void onCompleted(String dbName) { + Log.v(Helper.TAG,"onCompleted"); + Toasty.success(getApplicationContext(),getString(R.string.data_import_success_simple),Toast.LENGTH_LONG).show(); + Intent changeAccount = new Intent(activity, MainActivity.class); + changeAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + activity.finish(); + activity.startActivity(changeAccount); + } + + @Override + public void onError(Exception e) { + Log.v(Helper.TAG,"onError"); + e.printStackTrace(); + Toasty.error(getApplicationContext(),getString(R.string.data_import_error_simple),Toast.LENGTH_LONG).show(); + } + }); } } @@ -2362,7 +2455,7 @@ public abstract class BaseMainActivity extends BaseActivity if (typePosition.get(position) == RetrieveFeedsAsyncTask.Type.TAG) { if (tabLayout.getTabAt(position) != null && tabLayout.getTabAt(position).getText() != null) { - SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, DB_NAME, null, Sqlite.DB_VERSION).open(); List tagTimelines; tagTimelines = new SearchDAO(BaseMainActivity.this, db).getTabInfo(tabLayout.getTabAt(position).getText().toString()); @@ -2493,7 +2586,7 @@ public abstract class BaseMainActivity extends BaseActivity } String tabName = tabLayout.getTabAt(position).getText().toString().trim(); - SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, DB_NAME, null, Sqlite.DB_VERSION).open(); List tagTimelines = new SearchDAO(BaseMainActivity.this, db).getTabInfo(tabName); String tag; @@ -3001,7 +3094,7 @@ public abstract class BaseMainActivity extends BaseActivity private void displayFollowInstances(){ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, DB_NAME, null, Sqlite.DB_VERSION).open(); federatedTimelines = findViewById(R.id.federated_timeline); federatedTimelinesShow(); @@ -3043,7 +3136,7 @@ public abstract class BaseMainActivity extends BaseActivity public void onClick(View v) { if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) new ManageListsAsyncTask(BaseMainActivity.this, ManageListsAsyncTask.action.GET_LIST, null, null, null, null, BaseMainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(BaseMainActivity.this, DB_NAME, null, Sqlite.DB_VERSION).open(); new InstancesDAO(BaseMainActivity.this, db).cleanDoublon(); List remoteInstances = new InstancesDAO(BaseMainActivity.this, db).getAllInstances(); popup = new PopupMenu(BaseMainActivity.this, federatedTimelines); @@ -3243,7 +3336,7 @@ public abstract class BaseMainActivity extends BaseActivity dialogBuilder.setPositiveButton(R.string.validate, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), DB_NAME, null, Sqlite.DB_VERSION).open(); String instanceName = instance_list.getText().toString().trim(); new Thread(new Runnable(){ @Override diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java index 68d60cfa5..6b4baefe8 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java @@ -189,7 +189,7 @@ public class TootActivity extends BaseActivity implements OnPostActionInterface, private int currentCursorPosition, searchLength; private TextView toot_space_left; private String initialContent; - private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; + public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; private Account accountReply; private View popup_trans; private AlertDialog dialogTrans; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index e66603a7b..c3f12f2cf 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -3596,7 +3596,8 @@ public class API { while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); Emojis emojis1 = parseEmojis(resobj); - emojis.add(emojis1); + if( emojis1.isVisible_in_picker()) + emojis.add(emojis1); i++; } } catch (JSONException e) { @@ -3617,6 +3618,11 @@ public class API { emojis.setShortcode(resobj.get("shortcode").toString()); emojis.setStatic_url(resobj.get("static_url").toString()); emojis.setUrl(resobj.get("url").toString()); + try { + emojis.setVisible_in_picker((resobj.getBoolean("visible_in_picker"))); + }catch (Exception e){ + emojis.setVisible_in_picker(true); + } }catch (Exception ignored){} return emojis; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Emojis.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Emojis.java index 96388e84e..f215330fd 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Emojis.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Emojis.java @@ -27,27 +27,10 @@ public class Emojis implements Parcelable { private String shortcode; private String static_url; private String url; + private boolean visible_in_picker; public Emojis(){} - protected Emojis(Parcel in) { - shortcode = in.readString(); - static_url = in.readString(); - url = in.readString(); - } - - public static final Creator CREATOR = new Creator() { - @Override - public Emojis createFromParcel(Parcel in) { - return new Emojis(in); - } - - @Override - public Emojis[] newArray(int size) { - return new Emojis[size]; - } - }; - public String getShortcode() { return shortcode; } @@ -72,6 +55,14 @@ public class Emojis implements Parcelable { this.url = url; } + public boolean isVisible_in_picker() { + return visible_in_picker; + } + + public void setVisible_in_picker(boolean visible_in_picker) { + this.visible_in_picker = visible_in_picker; + } + @Override public int describeContents() { return 0; @@ -79,8 +70,28 @@ public class Emojis implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(shortcode); - dest.writeString(static_url); - dest.writeString(url); + dest.writeString(this.shortcode); + dest.writeString(this.static_url); + dest.writeString(this.url); + dest.writeByte(this.visible_in_picker ? (byte) 1 : (byte) 0); } + + protected Emojis(Parcel in) { + this.shortcode = in.readString(); + this.static_url = in.readString(); + this.url = in.readString(); + this.visible_in_picker = in.readByte() != 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public Emojis createFromParcel(Parcel source) { + return new Emojis(source); + } + + @Override + public Emojis[] newArray(int size) { + return new Emojis[size]; + } + }; } diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index 9d007a3d9..02a37d803 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -25,6 +25,14 @@ android:id="@+id/action_proxy" android:title="@string/proxy_set" app:showAsAction="never" /> + + Export statuses for %1$s %1$s toots out of %2$s have been exported. Something went wrong when exporting data for %1$s + Something went wrong when exporting data! + Data have been imported! + Something went wrong when importing data! Proxy Type @@ -852,6 +855,11 @@ %1$s published a new video: %2$s]]> %1$s has been blacklisted]]> %1$s has been unblacklisted]]> + Export data + Import Data + Select the file to import + An error occurred when selecting the backup file! + Please, don\'t kill the app while processing. That can\'t be quite long.