diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 6651510..7c75791 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -13,10 +13,10 @@ name: "CodeQL"
on:
push:
- branches: [ "master" ]
+ branches: [ "master", "dev" ]
pull_request:
# The branches below must be a subset of the branches above
- branches: [ "master" ]
+ branches: [ "master", "dev" ]
schedule:
- cron: '26 5 * * 0'
diff --git a/README.md b/README.md
index 598cd91..be6161f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,15 @@
+![repo preview](https://repository-images.githubusercontent.com/507594462/8b9d54af-1231-43e3-a84a-5d19da2e5e38)
# JsonList
-Android app for previewing JSON files in list form
+Introducing a Material You Android application for previewing JSON files in a user-friendly list format.
+
+![JsonList](images/jsonlist_main.jpg)
+
+Use the convenient split view feature that allows simultaneous display of the raw JSON string alongside the list representation.
+
+![JsonList split view](images/jsonlist_splitview.jpg)
+
+![JsonList split view landscape](images/jsonlist_splitview_landscape.jpg)
+
+Enjoy the flexibility of dynamic color support, adapting to your Material Design preferences.
+
+![JsonList dynamic colors](images/jsonlist_material_colots.gif)
diff --git a/app/build.gradle b/app/build.gradle
index b33b64d..ebc74ec 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,14 +3,14 @@ plugins {
}
android {
- compileSdk 31
+ compileSdk 34
defaultConfig {
applicationId "com.sjapps.jsonlist"
minSdk 23
- targetSdk 31
- versionCode 5
- versionName "1.3"
+ targetSdk 34
+ versionCode 10
+ versionName "1.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -22,7 +22,7 @@ android {
}
debug {
applicationIdSuffix '.debug'
- versionNameSuffix ' debug'
+ versionNameSuffix ' dev'
}
}
compileOptions {
@@ -33,14 +33,15 @@ android {
dependencies {
- implementation 'androidx.appcompat:appcompat:1.4.2'
- implementation 'com.google.android.material:material:1.6.1'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.12.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- implementation 'com.google.code.gson:gson:2.8.9'
- implementation 'com.github.slavce14:SJ-Library:1.4'
+ implementation 'com.google.code.gson:gson:2.10.1'
+ implementation 'com.github.slavce14:sj-dialog:1.6.1'
+ implementation "androidx.core:core-splashscreen:1.0.1"
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cce6d97..1f93d44 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,10 @@
+
+
+
+
+
+
@@ -38,6 +46,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_open_file-playstore.png b/app/src/main/ic_open_file-playstore.png
deleted file mode 100644
index a1f66a1..0000000
Binary files a/app/src/main/ic_open_file-playstore.png and /dev/null differ
diff --git a/app/src/main/java/com/sjapps/about/AboutActivity.java b/app/src/main/java/com/sjapps/about/AboutActivity.java
index f623421..cbbb4de 100644
--- a/app/src/main/java/com/sjapps/about/AboutActivity.java
+++ b/app/src/main/java/com/sjapps/about/AboutActivity.java
@@ -3,7 +3,10 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
@@ -12,16 +15,20 @@
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.sjapps.jsonlist.R;
+import com.sjapps.library.customdialog.ImageListItem;
+import com.sjapps.library.customdialog.ListDialog;
import java.util.ArrayList;
public class AboutActivity extends AppCompatActivity {
+ private static final String GITHUB_REPOSITORY_RELEASES = "https://github.com/SlaVcE14/JsonList/releases";
final String STORE_PACKAGE_NAME = "com.sjapps.sjstore";
ImageView logo;
@@ -29,6 +36,8 @@ public class AboutActivity extends AppCompatActivity {
RecyclerView ListRV,LibListRV;
ArrayList appInfoItems = new ArrayList<>();
ArrayList libsItems;
+ boolean isStoreInstalled;
+ Drawable storeIcon;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -69,13 +78,41 @@ void initialize(){
ListRV = findViewById(R.id.aboutList);
LibListRV = findViewById(R.id.LibrariesList);
nestedScrollView = findViewById(R.id.nestedList);
-
+ findViewById(R.id.updateBtn).setVisibility(View.VISIBLE);
if (CheckStoreIsInstalled()){
- findViewById(R.id.updateBtn).setVisibility(View.VISIBLE);
+ isStoreInstalled = true;
}
}
public void CheckForUpdate(View view) {
+ if (!isStoreInstalled) {
+ openGitHub();
+ return;
+ }
+
+ ListDialog dialog = new ListDialog();
+
+ ArrayList items = new ArrayList<>();
+ items.add(new ImageListItem("GitHub", AppCompatResources.getDrawable(this,R.drawable.github_logo), (ImageItemClick) this::openGitHub));
+ items.add(new ImageListItem("SJ Store", storeIcon, (ImageItemClick) this::openStore));
+
+ dialog.Builder(this,true)
+ .setTitle("Open...")
+ .setImageItems(items, (position, obj) -> {
+ if (obj.getData() == null)
+ return;
+ ((ImageItemClick) obj.getData()).onClick();
+ dialog.dismiss();
+ })
+ .show();
+ }
+
+ private void openGitHub(){
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(GITHUB_REPOSITORY_RELEASES));
+ startActivity(intent);
+ }
+
+ private void openStore(){
Intent intent = new Intent();
intent.setComponent(new ComponentName(STORE_PACKAGE_NAME,STORE_PACKAGE_NAME + ".AppActivity"));
intent.putExtra("packageName", getPackageName());
@@ -83,10 +120,11 @@ public void CheckForUpdate(View view) {
startActivity(intent);
}
- public boolean CheckStoreIsInstalled(){
+ private boolean CheckStoreIsInstalled(){
PackageManager packageManager = getPackageManager();
try {
- packageManager.getPackageInfo(STORE_PACKAGE_NAME,0);
+ PackageInfo packageInfo = packageManager.getPackageInfo(STORE_PACKAGE_NAME,0);
+ storeIcon = packageInfo.applicationInfo.loadIcon(packageManager);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
diff --git a/app/src/main/java/com/sjapps/about/ImageItemClick.java b/app/src/main/java/com/sjapps/about/ImageItemClick.java
new file mode 100644
index 0000000..52c3a14
--- /dev/null
+++ b/app/src/main/java/com/sjapps/about/ImageItemClick.java
@@ -0,0 +1,5 @@
+package com.sjapps.about;
+
+public interface ImageItemClick {
+ void onClick();
+}
diff --git a/app/src/main/java/com/sjapps/about/LibraryList.java b/app/src/main/java/com/sjapps/about/LibraryList.java
index fa1509e..9d46ca8 100644
--- a/app/src/main/java/com/sjapps/about/LibraryList.java
+++ b/app/src/main/java/com/sjapps/about/LibraryList.java
@@ -1,10 +1,14 @@
package com.sjapps.about;
+import com.google.gson.internal.GsonBuildConfig;
+import com.sjapps.library.BuildConfig;
+
public class LibraryList extends ListGenerator{
@Override
public void init() {
- addItem("SJ Library","1.4", "https://github.com/SlaVcE14/SJ-Library");
- addItem("gson","2.8.9", "https://github.com/google/gson");
+ addItem("SJ Dialog", BuildConfig.VERSION_NAME, "https://github.com/SlaVcE14/SJ-Dialog");
+ addItem("gson", GsonBuildConfig.VERSION, "https://github.com/google/gson");
+ addItem("core-splashscreen","1.0.1","https://developer.android.com/develop/ui/views/launch/splash-screen");
}
}
diff --git a/app/src/main/java/com/sjapps/adapters/ListAdapter.java b/app/src/main/java/com/sjapps/adapters/ListAdapter.java
index 60a2987..884ba6b 100644
--- a/app/src/main/java/com/sjapps/adapters/ListAdapter.java
+++ b/app/src/main/java/com/sjapps/adapters/ListAdapter.java
@@ -6,23 +6,87 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseAdapter;
+import android.view.animation.OvershootInterpolator;
import android.widget.TextView;
import android.widget.Toast;
-import com.sjapps.jsonlist.ListItem;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.sjapps.jsonlist.functions;
+import com.sjapps.jsonlist.java.JsonData;
+import com.sjapps.jsonlist.java.ListItem;
import com.sjapps.jsonlist.MainActivity;
import com.sjapps.jsonlist.R;
import java.util.ArrayList;
-public class ListAdapter extends BaseAdapter {
+public class ListAdapter extends RecyclerView.Adapter {
ArrayList list;
Context context;
MainActivity activity;
String path;
public int selectedItem = -1;
+ public int highlightedItem = -1;
+
+
+
+ static class ViewHolderShort extends RecyclerView.ViewHolder{
+
+ TextView title;
+
+ public ViewHolderShort(View itemView) {
+ super(itemView);
+ title = itemView.findViewById(R.id.itemName);
+ }
+ public TextView getTitleTxt(){
+ return title;
+ }
+
+ public View getView(){
+ return itemView;
+ }
+
+ }
+
+ static class ViewHolderLong extends RecyclerView.ViewHolder{
+
+ TextView title, value;
+
+ public ViewHolderLong(View itemView) {
+ super(itemView);
+ title = itemView.findViewById(R.id.itemName);
+ value = itemView.findViewById(R.id.itemValue);
+ }
+ public TextView getTitleTxt(){
+ return title;
+ }
+
+ public TextView getValueTxt(){
+ return value;
+ }
+
+ public View getView(){
+ return itemView;
+ }
+
+ }
+
+ static class ViewHolderSpace extends RecyclerView.ViewHolder{
+
+
+ public ViewHolderSpace(View itemView) {
+ super(itemView);
+
+ }
+
+ public View getView(){
+ return itemView;
+ }
+
+ }
+
public ListAdapter(ArrayList list, Context context,String path){
this.list = list;
@@ -32,73 +96,117 @@ public ListAdapter(ArrayList list, Context context,String path){
}
@Override
- public int getCount() {
- if (list == null)
- return 0;
+ public int getItemViewType(int position) {
- if (list.size() == 0)
- return 0;
+ ListItem item = list.get(position);
- if (!list.get(getLast()).isSpace())
- return list.size();
- return getLast();
+ return (item.isArray() || item.isObject())?0:item.isSpace()?2:1;
}
+ @NonNull
@Override
- public Object getItem(int i) {
- return list.get(i);
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ switch (viewType) {
+ case 0:
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout,parent,false);
+ return new ViewHolderShort(view);
+ case 1:
+ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout2,parent,false);
+ return new ViewHolderLong(view);
+ case 2:
+ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.space_layout,parent,false);
+ return new ViewHolderSpace(view);
+ }
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout,parent,false);
+ return new ViewHolderShort(view);
}
@Override
- public long getItemId(int i) {
- return i;
- }
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int pos) {
- private int getLast(){
- return (list.size()>0?list.size()-1:0);
- }
+ ListItem item = list.get(pos);
+ if(item.isSpace()) {
+ return;
+ }
+ int position = pos;
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
+ if (item.isArray() || item.isObject()) {
- ListItem item = list.get(position);
- if(item.isSpace()) {
- return LayoutInflater.from(context).inflate(R.layout.space_layout, parent, false);
- }
- if (item.isArrayOfObjects() || item.isObject()) {
+ ViewHolderShort currentHolder = (ViewHolderShort) holder;
- View view = LayoutInflater.from(context).inflate(R.layout.list_layout,parent,false);
- TextView titleTxt = view.findViewById(R.id.itemName);
+ TextView titleTxt = currentHolder.getTitleTxt();
titleTxt.setText(item.getName());
+ View view = currentHolder.getView();
+
if (selectedItem == position){
view.findViewById(R.id.copyBtn).setVisibility(View.VISIBLE);
+ }else view.findViewById(R.id.copyBtn).setVisibility(View.GONE);
+
+ if (highlightedItem == position){
+ functions.setAnimation(context,view,R.anim.button_prev,new OvershootInterpolator());
+ highlightedItem = -1;
}
- view.findViewById(R.id.btn).setOnClickListener(view1 -> activity.open(item.getName(),path + (path.equals("") ? "":"///") + item.getName()));
+
+ String newPath = path + (path.equals("") ? "": "///" + (item.getId()!=-1?"{" + item.getId() + "}":"")) + item.getName();
+ view.findViewById(R.id.btn).setOnClickListener(view1 -> activity.open(JsonData.getPathFormat(newPath),newPath,position));
view.findViewById(R.id.copyBtn).setOnClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clipData = ClipData.newPlainText("Text",item.getName());
- clipboard.setPrimaryClip(clipData);
- Toast.makeText(v.getContext(),"Copied to clipboard",Toast.LENGTH_SHORT).show();
- selectedItem = -1;
- notifyDataSetChanged();
+ ClipData clipData = ClipData.newPlainText("Text",item.getName());
+ clipboard.setPrimaryClip(clipData);
+ Toast.makeText(v.getContext(),"Copied to clipboard",Toast.LENGTH_SHORT).show();
+ selectedItem = -1;
+ notifyItemChanged(position);
});
view.findViewById(R.id.btn).setOnLongClickListener(v -> {
+ notifyItemChanged(selectedItem);
selectedItem = position;
- notifyDataSetChanged();
+ notifyItemChanged(position);
return true;
});
- return view;
+ return;
+ }
+ ViewHolderLong currentHolder = (ViewHolderLong) holder;
+ View view = currentHolder.getView();
+ TextView titleTxt = currentHolder.getTitleTxt();
+ TextView valueTxt = currentHolder.getValueTxt();
+ if (item.getName() == null)
+ titleTxt.setVisibility(View.GONE);
+ else {
+ titleTxt.setVisibility(View.VISIBLE);
+ titleTxt.setText(item.getName());
}
- View view = LayoutInflater.from(context).inflate(R.layout.list_layout2,parent,false);
- TextView titleTxt = view.findViewById(R.id.itemName);
- TextView valueTxt = view.findViewById(R.id.itemValue);
view.setClickable(false);
- titleTxt.setText(item.getName());
- valueTxt.setText(item.getValue());
- return view;
+ valueTxt.setText(item.getValue().isEmpty() ? "\"\"" : item.getValue());
+
+ }
+
+ @Override
+ public int getItemCount() {
+ if (list == null)
+ return 0;
+
+ if (list.size() == 0)
+ return 0;
+
+ if (!list.get(getLast()).isSpace())
+ return list.size();
+ return getLast();
+ }
+
+ private int getLast(){
+ return (list.size()>0?list.size()-1:0);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public void setHighlightItem(int position){
+ highlightedItem = position;
}
}
diff --git a/app/src/main/java/com/sjapps/jsonlist/AppState.java b/app/src/main/java/com/sjapps/jsonlist/AppState.java
new file mode 100644
index 0000000..d1ffdf7
--- /dev/null
+++ b/app/src/main/java/com/sjapps/jsonlist/AppState.java
@@ -0,0 +1,30 @@
+package com.sjapps.jsonlist;
+
+public class AppState {
+ boolean hasNewCrash;
+ boolean hasCrashLogs;
+
+ public boolean hasNewCrash() {
+ return hasNewCrash;
+ }
+
+ public void setHasNewCrash(boolean hasNewCrash) {
+ this.hasNewCrash = hasNewCrash;
+ }
+
+ public boolean hasCrashLogs() {
+ return hasCrashLogs;
+ }
+
+ public void setHasCrashLogs(boolean hasCrashLogs) {
+ this.hasCrashLogs = hasCrashLogs;
+ }
+
+ @Override
+ public String toString() {
+ return "AppState{" +
+ "hasNewCrash=" + hasNewCrash +
+ ", hasCrashLogs=" + hasCrashLogs +
+ '}';
+ }
+}
diff --git a/app/src/main/java/com/sjapps/jsonlist/FileSystem.java b/app/src/main/java/com/sjapps/jsonlist/FileSystem.java
index dc6af44..31bb6b4 100644
--- a/app/src/main/java/com/sjapps/jsonlist/FileSystem.java
+++ b/app/src/main/java/com/sjapps/jsonlist/FileSystem.java
@@ -1,14 +1,28 @@
package com.sjapps.jsonlist;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
import android.net.Uri;
+
+import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import java.io.FileInputStream;
+import com.sjapps.logs.CrashLogs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
-import java.util.Scanner;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.Path;
+
public class FileSystem {
+ final static String LogFile = "Log.json";
+ final static String StateFile = "CheckState.json";
public static JsonObject loadDataToJsonObj(JsonElement data){
return data.getAsJsonObject();
@@ -17,41 +31,119 @@ public static JsonArray loadDataToJsonArray(JsonElement data) {
return data.getAsJsonArray();
}
- public static String LoadDataFromFile(MainActivity mainActivity, Uri uri) {
+ public static String LoadDataFromFile(Context context, Uri uri, InputStream inputStream, AssetFileDescriptor fileDescriptor) {
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ String path = uri.getPath();
+ if (path.contains("../"))
+ throw new SecurityException();
+ Path normalized = java.nio.file.FileSystems.getDefault().getPath(path).normalize();
+ if (normalized.startsWith("/data"))
+ throw new SecurityException();
+ }
StringBuilder sb = new StringBuilder();
- FileInputStream inputStream = null;
- Scanner sc = null;
try {
- inputStream = (FileInputStream) mainActivity.getContentResolver().openInputStream(uri);
- sc = new Scanner(inputStream, "UTF-8");
-
- while (sc.hasNextLine()) {
- String line = sc.nextLine();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+ long currentBytes = 0;
+ long fileSize = fileDescriptor.getLength();
+ String line;
+ while ((line = reader.readLine()) != null) {
sb.append(line);
+ currentBytes += line.length();
+ ((MainActivity) context).updateProgress((int)((currentBytes/(float)fileSize)*100));
}
- if (sc.ioException() != null) {
- throw sc.ioException();
- }
-
+ fileDescriptor.close();
+ inputStream.close();
+ reader.close();
+ return sb.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
+ }
+ }
+
+ static String LoadData(Context context,String FileName){
+ File file = new File(context.getFilesDir(), FileName);
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
}
- if (sc != null) {
- sc.close();
+ FileReader fileReader = new FileReader(file.getAbsolutePath());
+
+
+ StringBuilder builder = new StringBuilder();
+ String jsonString = null;
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+ while ((jsonString = bufferedReader.readLine()) != null) {
+ builder.append(jsonString);
}
- return sb.toString();
+ bufferedReader.close();
+ if (builder.toString().equals("") || builder.toString().equals("{}"))
+ return null;
+
+ return new String(builder);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ static void SaveData(Context context, String FileName, String data){
+ File file = new File(context.getFilesDir(), FileName);
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ FileWriter fileWriter = new FileWriter(file);
+ fileWriter.write(data);
+ fileWriter.close();
+ }catch (IOException e){
+ e.printStackTrace();
+ }
+ }
+
+ public static AppState loadStateData(Context context) {
+ String data = LoadData(context, StateFile);
+ if (data == null)
+ return new AppState();
+ return new Gson().fromJson(data, AppState.class);
+ }
+
+ public static CrashLogs loadLogData(Context context){
+ String data = LoadData(context,LogFile);
+ if (data == null)
+ return new CrashLogs();
+ return new Gson().fromJson(data,CrashLogs.class);
+ }
+
+ public static void SaveState(Context context, String data) {
+ SaveData(context,StateFile,data);
+ }
+
+ public static void SaveLog(Context context, String data) {
+ SaveData(context,LogFile,data);
+ }
+ public static File createTempFile(Context context, String data, String fileName) {
+ File file = new File(context.getCacheDir(),fileName);
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ FileWriter fileWriter = new FileWriter(file);
+ fileWriter.write(data);
+ fileWriter.close();
+ }catch (IOException e){
+ e.printStackTrace();
}
+ return file;
+ }
+
+ public static boolean deleteTempFile(Context context, String fileName){
+ File file = new File(context.getCacheDir(),fileName);
+ return file.delete();
}
}
diff --git a/app/src/main/java/com/sjapps/jsonlist/MainActivity.java b/app/src/main/java/com/sjapps/jsonlist/MainActivity.java
index e37f1e8..549adea 100644
--- a/app/src/main/java/com/sjapps/jsonlist/MainActivity.java
+++ b/app/src/main/java/com/sjapps/jsonlist/MainActivity.java
@@ -1,74 +1,116 @@
package com.sjapps.jsonlist;
-import androidx.activity.result.ActivityResult;
-import androidx.activity.result.ActivityResultCallback;
+import static com.sjapps.jsonlist.java.JsonFunctions.*;
+
+import androidx.activity.OnBackPressedCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import android.app.Activity;
+import android.content.ClipData;
import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.transition.AutoTransition;
import android.transition.TransitionManager;
import android.util.Log;
+import android.util.TypedValue;
+import android.view.DragAndDropPermissions;
+import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.OvershootInterpolator;
import android.widget.Button;
import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.ProgressBar;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
-
+import com.google.android.material.progressindicator.LinearProgressIndicator;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+
import com.sjapps.about.AboutActivity;
import com.sjapps.adapters.ListAdapter;
-import com.sjapps.library.customdialog.SetupDialog;
-
+import com.sjapps.jsonlist.java.JsonData;
+import com.sjapps.jsonlist.java.JsonFunctions;
+import com.sjapps.jsonlist.java.ListItem;
+import com.sjapps.library.customdialog.BasicDialog;
+import com.sjapps.logs.CustomExceptionHandler;
+import com.sjapps.logs.LogActivity;
+
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
-import java.util.Set;
public class MainActivity extends AppCompatActivity {
final String TAG = "MainActivity";
- ImageButton backBtn, menuBtn;
+ ImageButton backBtn, menuBtn, splitViewBtn;
+ ImageView fileImg;
Button openFileBtn;
- TextView titleTxt, emptyListTxt;
- ListView list;
- String path = "";
- ProgressBar progressBar;
- TextView loadingFileTxt;
- boolean isMenuOpen;
+ TextView titleTxt, emptyListTxt, jsonTxt;
+ RecyclerView list;
+ JsonData data = new JsonData();
+ LinearLayout progressView, mainLL;
+ LinearProgressIndicator progressBar;
+ boolean isMenuOpen, showJson, isRawJsonLoaded, isVertical = true;
ListAdapter adapter;
- ArrayList rootList = new ArrayList<>();
- View menu, dim_bg;
+ View menu, dim_bg, jsonView;
ViewGroup viewGroup;
AutoTransition autoTransition = new AutoTransition();
Handler handler = new Handler();
+ Thread readFileThread;
+ RelativeLayout dropTarget;
@Override
protected void onResume() {
super.onResume();
+ checkCrashLogs();
Log.d(TAG, "onResume: resume");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler))
+ Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
+
setContentView(R.layout.activity_main);
initialize();
+
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
+ isVertical = false;
+ mainLL.setOrientation(LinearLayout.HORIZONTAL);
+ }
+
+ functions.setAnimation(this,fileImg,R.anim.scale_in_file_img, new DecelerateInterpolator());
+ functions.setAnimation(this,openFileBtn,R.anim.button_pop, new OvershootInterpolator());
+
autoTransition.setDuration(150);
menuBtn.setOnClickListener(view -> open_closeMenu());
- backBtn.setOnClickListener(view -> onBackPressed());
+ backBtn.setOnClickListener(view -> {
+ if(!data.isEmptyPath()) getOnBackPressedDispatcher().onBackPressed();
+ });
openFileBtn.setOnClickListener(view -> ImportFromFile());
menu.findViewById(R.id.openFileBtn2).setOnClickListener(view -> {
@@ -79,92 +121,195 @@ protected void onCreate(Bundle savedInstanceState) {
OpenAbout();
open_closeMenu();
});
+ menu.findViewById(R.id.logBtn).setOnClickListener(view -> {
+ OpenLogPage();
+ open_closeMenu();
+ });
dim_bg.setOnClickListener(view -> open_closeMenu());
-
-
- Intent intent = getIntent();
+ splitViewBtn.setOnClickListener(view -> open_closeSplitView());
+ Intent intent = getIntent();
Log.d(TAG, "onCreate: " + intent);
- if (Intent.ACTION_VIEW.equals(intent.getAction())){
-
- Log.d(TAG, "onCreate: " + intent.getData());
-
- String FileData = FileSystem.LoadDataFromFile(this,intent.getData());
-// Log.d(TAG, "onCreate: " + FileData);
- if (FileData != null)
- LoadData(FileData);
- else
- Log.d(TAG, "onCreate: null data");
+ if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ ReadFile(intent.getData());
}
if (intent.getAction().equals("android.intent.action.OPEN_FILE")){
ImportFromFile();
}
- }
- private void OpenAbout() {
- startActivity(new Intent(MainActivity.this, AboutActivity.class));
- }
+ dropTarget.setOnDragListener((v, event) -> {
- @Override
- public void onBackPressed() {
+ TextView dropTargetTxt = v.findViewById(R.id.dropTargetText);
+ View dropTargetBackground = v.findViewById(R.id.dropTargetBackground);
- if (isMenuOpen) {
- open_closeMenu();
- return;
- }
+ String MIMEType = Build.VERSION.SDK_INT > Build.VERSION_CODES.P?"application/json":"application/*";
- if (adapter.selectedItem != -1){
- adapter.selectedItem = -1;
- adapter.notifyDataSetChanged();
- return;
- }
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ dropTarget.setAlpha(1);
+ return true;
- if(path.equals("")){
- SetupDialog dialog = new SetupDialog();
- String Title = "Exit?";
- String btn2Txt = "Yes";
- dialog.Short(this,Title,btn2Txt)
- .onButtonClick(this::finish).show();
- return;
- }
+ case DragEvent.ACTION_DRAG_ENTERED:
+ if(event.getClipDescription().getMimeTypeCount() > 1){
+ dropTargetTxt.setText(R.string.only_one_file_is_allowed);
+ dropTargetBackground.setBackgroundColor(setColor(R.attr.colorError));
+ dropTargetBackground.setAlpha(.8f);
+ return false;
+ }
+ if (!event.getClipDescription().hasMimeType(MIMEType)) {
+ dropTargetTxt.setText(R.string.this_is_not_json_file);
+ dropTargetBackground.setBackgroundColor(setColor(R.attr.colorError));
+ dropTargetBackground.setAlpha(.8f);
+ return false;
+ }
+ dropTargetBackground.setBackgroundColor(setColor(R.attr.colorPrimary));
+ dropTargetBackground.setAlpha(.8f);
+ return true;
+
+ case DragEvent.ACTION_DRAG_EXITED:
+ dropTargetTxt.setText(R.string.drop_json_file_here);
+ dropTargetBackground.setBackgroundColor(setColor(R.attr.colorOnBackground));
+ dropTargetBackground.setAlpha(.5f);
+ return true;
+
+ case DragEvent.ACTION_DRAG_ENDED:
+ dropTargetTxt.setText(R.string.drop_json_file_here);
+ dropTargetBackground.setBackgroundColor(setColor(R.attr.colorOnBackground));
+ dropTarget.setAlpha(0);
+ return true;
+
+ case DragEvent.ACTION_DROP:
+ if (event.getClipData().getItemCount() > 1){
+ return false;
+ }
+ if (!event.getClipDescription().hasMimeType(MIMEType))
+ return false;
+ if (readFileThread != null && readFileThread.isAlive()) {
+ Snackbar.make(getWindow().getDecorView(),"Loading file in progress, try again later", BaseTransientBottomBar.LENGTH_SHORT).show();
+ return false;
+ }
- TransitionManager.beginDelayedTransition(viewGroup,autoTransition);
+ ClipData.Item item = event.getClipData().getItemAt(0);
+ DragAndDropPermissions dropPermissions = null;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
+ dropPermissions = requestDragAndDropPermissions(event);
+ ReadFile(item.getUri());
- String[] pathStrings = path.split("///");
- path = "";
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N && dropPermissions != null)
+ dropPermissions.release();
- String Title = "";
- for(int i = 0; i < pathStrings.length-1; i++) {
- path = path.concat((path.equals("")?"":"///") + pathStrings[i]);
+ return true;
+ }
+ return false;
+ });
+ }
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
+ isVertical = false;
+ mainLL.setOrientation(LinearLayout.HORIZONTAL);
+ }else {
+ isVertical = true;
+ mainLL.setOrientation(LinearLayout.VERTICAL);
}
- if (pathStrings.length > 1) {
- Title = pathStrings[pathStrings.length-2];
+ }
+
+ void checkCrashLogs() {
+
+ AppState state = FileSystem.loadStateData(this);
+ TextView logBtn = menu.findViewById(R.id.logBtn);
+ if (!state.hasCrashLogs()) {
+ logBtn.setVisibility(View.GONE);
+ return;
}
+ logBtn.setVisibility(View.VISIBLE);
- open(Title, path);
- if(path.equals("")) {
- backBtn.setVisibility(View.GONE);
+ TypedValue typedValue = new TypedValue();
+
+ if (state.hasNewCrash()) {
+ getTheme().resolveAttribute(R.attr.colorOnError, typedValue, true);
+ logBtn.setTextColor(typedValue.data);
+ logBtn.setBackgroundResource(R.drawable.ripple_red);
+ menuBtn.setImageResource(R.drawable.menu_with_dot);
+ return;
}
+ getTheme().resolveAttribute(R.attr.colorOnSurfaceVariant, typedValue, true);
+ logBtn.setTextColor(typedValue.data);
+ logBtn.setBackgroundResource(R.drawable.ripple_list2);
+ menuBtn.setImageResource(R.drawable.ic_menu);
+ }
+
+ private void OpenAbout() {
+ startActivity(new Intent(MainActivity.this, AboutActivity.class));
}
- private void initialize(){
+ private void OpenLogPage() {
+ startActivity(new Intent(MainActivity.this, LogActivity.class));
+ }
+
+ OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
+ @Override
+ public void handleOnBackPressed() {
+ if (isMenuOpen) {
+ open_closeMenu();
+ return;
+ }
+
+ if (adapter!= null && adapter.selectedItem != -1){
+ adapter.selectedItem = -1;
+ adapter.notifyItemRangeChanged(0,adapter.getItemCount());
+ return;
+ }
+
+ if (data.isEmptyPath()){
+ BasicDialog dialog = new BasicDialog();
+ dialog.Builder(MainActivity.this, true)
+ .setTitle("Exit?")
+ .setRightButtonText("Yes")
+ .onButtonClick(() ->{
+ dialog.dismiss();
+ MainActivity.this.finish();
+ })
+ .show();
+ return;
+ }
+ TransitionManager.endTransitions(viewGroup);
+ TransitionManager.beginDelayedTransition(viewGroup, autoTransition);
+ data.goBack();
+ open(JsonData.getPathFormat(data.getPath()), data.getPath(),-1);
+ if (data.isEmptyPath()) {
+ backBtn.setVisibility(View.GONE);
+ }
+ }
+ };
+
+ private void initialize() {
+ getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
backBtn = findViewById(R.id.backBtn);
menuBtn = findViewById(R.id.menuBtn);
+ mainLL = findViewById(R.id.mainLL);
+ splitViewBtn = findViewById(R.id.splitViewBtn);
titleTxt = findViewById(R.id.titleTxt);
+ jsonTxt = findViewById(R.id.jsonTxt);
emptyListTxt = findViewById(R.id.emptyListTxt);
list = findViewById(R.id.list);
openFileBtn = findViewById(R.id.openFileBtn);
viewGroup = findViewById(R.id.content);
menu = findViewById(R.id.menu);
dim_bg = findViewById(R.id.dim_layout);
+ progressView = findViewById(R.id.loadingView);
progressBar = findViewById(R.id.progressBar);
- loadingFileTxt = findViewById(R.id.LoadFileTxt);
+ fileImg = findViewById(R.id.fileImg);
dim_bg.bringToFront();
menu.bringToFront();
+ jsonView = findViewById(R.id.rawJsonView);
menuBtn.bringToFront();
+ dropTarget = findViewById(R.id.dropTarget);
+ list.setLayoutManager(new LinearLayoutManager(this));
}
private void open_closeMenu() {
@@ -184,248 +329,291 @@ private void open_closeMenu() {
}
- private void LoadData(String Data){
+ private void LoadData(String Data) {
- progressBar.setVisibility(View.VISIBLE);
+ loadingStarted("loading json");
+ emptyListTxt.setVisibility(View.GONE);
- new Thread(() -> {
- ArrayList temp = rootList;
+ readFileThread = new Thread(() -> {
+ ArrayList temp = data.getRootList();
JsonElement element;
try {
element = JsonParser.parseString(Data);
- }catch (OutOfMemoryError | Exception e){
+ } catch (OutOfMemoryError e) {
e.printStackTrace();
- handler.post(() -> {
- Toast.makeText(MainActivity.this, "File is too large", Toast.LENGTH_SHORT).show();
- progressBar.setVisibility(View.GONE);
- });
+ fileTooLargeException();
return;
- }
- if (element == null){
- handler.post(() -> {
- Toast.makeText(MainActivity.this, "Filed to load file!", Toast.LENGTH_SHORT).show();
- progressBar.setVisibility(View.GONE);
- });
+ } catch (Exception e){
+ e.printStackTrace();
+ fileNotLoadedException();
return;
}
-
- if (element instanceof JsonObject){
- Log.d(TAG, "run: Json object");
- JsonObject object = FileSystem.loadDataToJsonObj(element);
- Log.d(TAG, "LoadData: " + object);
- rootList = getJsonObject(object);
+ if (element == null) {
+ fileNotLoadedException();
+ return;
}
- if (element instanceof JsonArray){
- Log.d(TAG, "run: Json array");
- JsonArray array = FileSystem.loadDataToJsonArray(element);
- Log.d(TAG, "LoadData: " + array);
- rootList = getJsonArrayRoot(array);
+ readFileThread.setName("readFileThread");
+ handler.post(()-> loadingStarted("creating list"));
+ try {
+ if (element instanceof JsonObject) {
+ Log.d(TAG, "run: Json object");
+ JsonObject object = FileSystem.loadDataToJsonObj(element);
+ data.setRootList(getJsonObject(object));
+ }
+ if (element instanceof JsonArray) {
+ Log.d(TAG, "run: Json array");
+ JsonArray array = FileSystem.loadDataToJsonArray(element);
+ data.setRootList(getJsonArrayRoot(array));
+ }
+ if (Data.length()<500000)
+ data.setRawData(Data);
+ else data.setRawData("-1");
+ data.clearPreviousPos();
+ } catch (Exception e){
+ e.printStackTrace();
+ creatingListException();
+ data.setRootList(null);
}
- if (rootList != null) {
+ if (!data.isRootListNull()) {
handler.post(() -> {
- TransitionManager.beginDelayedTransition(viewGroup,autoTransition);
- adapter = new ListAdapter(rootList, MainActivity.this, "");
+ TransitionManager.beginDelayedTransition(viewGroup, autoTransition);
+ adapter = new ListAdapter(data.getRootList(), MainActivity.this, "");
list.setAdapter(adapter);
+ fileImg.clearAnimation();
+ openFileBtn.clearAnimation();
+ fileImg.setVisibility(View.GONE);
openFileBtn.setVisibility(View.GONE);
+ functions.setAnimation(MainActivity.this,list,R.anim.scale_in2,new DecelerateInterpolator());
+ list.setVisibility(View.VISIBLE);
backBtn.setVisibility(View.GONE);
titleTxt.setText("");
- path = "";
+ data.clearPath();
});
- }else rootList = temp;
+ } else data.setRootList(temp);
+ isRawJsonLoaded = false;
+ if (showJson)
+ handler.post(this::ShowJSON);
+ else handler.post(() -> loadingFinished(true));
- handler.post(() -> progressBar.setVisibility(View.GONE));
-
- }).start();
+ });
+ readFileThread.start();
+ }
+ public void open(String Title, String path, int previousPosition) {
+ TransitionManager.endTransitions(viewGroup);
+ TransitionManager.beginDelayedTransition(viewGroup, autoTransition);
- }
- ArrayList getJsonArrayRoot(JsonArray array) {
- ArrayList Mainlist = new ArrayList<>();
- ListItem item = new ListItem();
- item.setName("Json Array");
- item.setIsArrayOfObjects(true);
- item.setListObjects(getJsonArray(array));
- Mainlist.add(item);
-
- return Mainlist;
- }
+ if (isMenuOpen)
+ open_closeMenu();
- ArrayList> getJsonArray(JsonArray array) {
- ArrayList> ArrList = new ArrayList<>();
- for (int i = 0; i < array.size(); i++) {
- if (array.get(i) instanceof JsonObject) {
- ArrayList ListOfItems = getJsonObject((JsonObject) array.get(i));
- ArrList.add(ListOfItems);
- }
- }
+ if (emptyListTxt.getVisibility() == View.VISIBLE)
+ emptyListTxt.setVisibility(View.GONE);
- return ArrList;
+ data.setPath(path);
+ titleTxt.setText(Title);
+ ArrayList arrayList = getListFromPath(path,data.getRootList());
+ adapter = new ListAdapter(arrayList, this, path);
+ list.setAdapter(adapter);
- }
+ if (previousPosition == -1) {
+ handler.postDelayed(() -> {
+ list.smoothScrollToPosition(data.getPreviousPos()+2);
+ adapter.setHighlightItem(data.getPreviousPos());
+ }, 500);
+ handler.postDelayed(() -> {
+ adapter.notifyItemChanged(data.getPreviousPos());
+ }, 600);
+ }
+ else data.addPreviousPos(previousPosition);
- boolean isArrayHasObjects(JsonArray array){
- for (int i = 0; i < array.size(); i++) {
- if (!(array.get(i) instanceof JsonObject)) {
- return false;
- }
+ if (arrayList.size() == 0) {
+ emptyListTxt.setVisibility(View.VISIBLE);
+ }
+ System.out.println("path = " + path);
+ if (!path.equals("")) {
+ backBtn.setVisibility(View.VISIBLE);
}
- return true;
}
- ArrayList getJsonObject(JsonObject obj) {
+ private void open_closeSplitView(){
+ TransitionManager.endTransitions(viewGroup);
+ TransitionManager.beginDelayedTransition(viewGroup, autoTransition);
- ArrayList Mainlist = new ArrayList<>();
- Set keys = obj.keySet();
+ if (showJson){
+ functions.setAnimation(this,jsonView,isVertical?R.anim.slide_bottom_out:R.anim.slide_right_out,new AccelerateDecelerateInterpolator());
+ handler.postDelayed(()-> jsonView.setVisibility(View.GONE),400);
+ showJson = false;
+ return;
+ }
+ showJson = true;
+ jsonView.setVisibility(View.VISIBLE);
+ functions.setAnimation(this,jsonView,isVertical?R.anim.slide_bottom_in:R.anim.slide_right_in,new DecelerateInterpolator());
+ if (!isRawJsonLoaded)
+ ShowJSON();
- Object[] keysArray = keys.toArray();
+ }
- try {
+ private void ShowJSON(){
+ if (data.getRawData().equals("-1")) {
+ Snackbar.make(getWindow().getDecorView(),"File is to large to be shown in a split screen!", BaseTransientBottomBar.LENGTH_SHORT).show();
+ if (progressView.getVisibility() == View.VISIBLE)
+ loadingFinished(true);
+ if (showJson)
+ open_closeSplitView();
+ return;
+ }
+ if (data.getRawData().equals(""))
+ return;
- for (Object o : keysArray) {
- ListItem item = new ListItem();
- item.setName(o.toString());
-
- if (obj.get(o.toString()) instanceof JsonObject) {
- item.setIsObject(true);
- ArrayList objList = getJsonObject((JsonObject) obj.get(o.toString()));
- item.setObjects(objList);
-
- } else if (obj.get(o.toString()) instanceof JsonArray) {
- JsonArray array = (JsonArray) obj.get(o.toString());
- Log.d(TAG, "isArrayHasObjects: " + isArrayHasObjects(array));
- if (isArrayHasObjects(array)){
- item.setIsArrayOfObjects(true);
- ArrayList> ArrList = getJsonArray(array);
- item.setListObjects(ArrList);
- }else{
- item.setIsArray(true);
- item.setValue(array.toString());
- }
+ loadingStarted("Displaying json...");
- } else {
+ Thread thread = new Thread(() -> {
+ String dataStr = JsonFunctions.getAsPrettyPrint(data.getRawData());
+ handler.post(()-> {
+ jsonTxt.setText(dataStr);
+ loadingFinished(true);
+ isRawJsonLoaded = true;
+ });
+ });
+ thread.setName("loadingJson");
+ thread.start();
- item.setValue(obj.get(o.toString()).toString());
+ }
- }
- Mainlist.add(item);
- }
- }catch (Exception e){
- Log.e(TAG, "getJsonObject: Failed to load data");
- handler.post(() -> Toast.makeText(MainActivity.this, "Failed to load data!", Toast.LENGTH_SHORT).show());
- return null;
+ private void ImportFromFile() {
+ if (readFileThread != null && readFileThread.isAlive()) {
+ Snackbar.make(getWindow().getDecorView(),"Loading file in progress, try again later", BaseTransientBottomBar.LENGTH_SHORT).show();
+ return;
}
- return Mainlist;
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType(Build.VERSION.SDK_INT > Build.VERSION_CODES.P?"application/json":"application/*");
+ ActivityResultData.launch(intent);
}
- ArrayList getArrayList(ArrayList> list){
- ArrayList newList = new ArrayList<>();
- for(ArrayList lists : list) {
- newList.addAll(lists);
- newList.add(new ListItem().Space());
- }
- return newList;
- }
- ArrayList getListFromPath(){
+ ActivityResultLauncher ActivityResultData = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() != Activity.RESULT_OK) {
+ if(result.getResultCode() == Activity.RESULT_CANCELED){
+ Toast.makeText(MainActivity.this,"Import data canceled",Toast.LENGTH_SHORT).show();
+ }
+ return;
+ }
+ if (result.getData() == null || result.getData().getData() == null){
+ Toast.makeText(MainActivity.this, "Fail to load data", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ //File
+ ReadFile(result.getData().getData());
+ });
- String[] pathStrings = path.split("///");
+ void ReadFile(Uri uri){
+ if (readFileThread != null && readFileThread.isAlive()){
+ return;
+ }
+ loadingStarted("Reading file");
- ArrayList list = rootList;
+ try {
+ InputStream inputStream = getContentResolver().openInputStream(uri);
+ AssetFileDescriptor fileDescriptor = getContentResolver().openAssetFileDescriptor(uri , "r");
- for (String pathString : pathStrings) {
+ readFileThread = new Thread(() -> {
- for (ListItem item : list) {
+ String Data = FileSystem.LoadDataFromFile(MainActivity.this, uri, inputStream, fileDescriptor);
- if (item.getName() == null || !item.getName().equals(pathString)) {
- continue;
+ if (Data == null) {
+ Log.d(TAG, "ReadFile: null data");
+ return;
}
+ handler.post(() -> {
+ LoadData(Data);
+ });
- if (item.isArrayOfObjects()) {
- list = getArrayList(item.getListObjects());
- break;
- }
- list = list.get(list.indexOf(item)).getObjects();
- if (list == null)
- list = new ArrayList<>();
- break;
- }
+ });
+ readFileThread.setName("readFileThread");
+ readFileThread.start();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
- return list;
-
}
- public void open(String Title,String path) {
- TransitionManager.beginDelayedTransition(viewGroup,autoTransition);
+ void loadingStarted(){
+ loadingStarted("loading...");
- if (isMenuOpen)
- open_closeMenu();
+ }
- if (emptyListTxt.getVisibility() == View.VISIBLE)
- emptyListTxt.setVisibility(View.GONE);
+ void loadingStarted(String txt){
+ TextView text = progressView.findViewById(R.id.loadingTxt);
+ progressBar.setIndeterminate(true);
+ text.setText(txt);
+ handler.postDelayed(() -> {
+ if (progressView.getVisibility() != View.VISIBLE) {
+ functions.setAnimation(this, progressView, R.anim.scale_in);
+ text.setVisibility(View.VISIBLE);
+ progressView.setVisibility(View.VISIBLE);
+ }
+ },300);
- this.path = path;
- titleTxt.setText(Title);
- ArrayList arrayList = getListFromPath();
- adapter = new ListAdapter(arrayList, this, path);
- list.setAdapter(adapter);
- if (arrayList.size() == 0){
- emptyListTxt.setVisibility(View.VISIBLE);
- }
- System.out.println("path = " + path);
- if(!path.equals("")) {
- backBtn.setVisibility(View.VISIBLE);
- }
+ }
+ public void updateProgress(int val){
+ handler.post(()->{
+ progressBar.setProgressCompat(val,true);
+ });
}
- private void ImportFromFile() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("application/json");
- ActivityResultData.launch(intent);
+ void loadingFinished(boolean isFinished){
+
+ if (!isFinished){
+ handler.postDelayed(()-> {
+ functions.setAnimation(this, progressView,R.anim.scale_out);
+ progressView.setVisibility(View.INVISIBLE);
+ },300);
+ return;
+ }
+
+ progressBar.setIndeterminate(false);
+ progressBar.setProgressCompat(100,true);
+
+ TextView text = progressView.findViewById(R.id.loadingTxt);
+ handler.postDelayed(() -> text.setText( "finished"),500);
+ handler.postDelayed(() -> {
+ },700);
+ handler.postDelayed(() -> text.setVisibility(View.INVISIBLE),900);
+ handler.postDelayed(() -> {
+ functions.setAnimation(this, progressView,R.anim.scale_out);
+ progressView.setVisibility(View.INVISIBLE);
+ },1000);
}
- ActivityResultLauncher ActivityResultData = registerForActivityResult(
- new ActivityResultContracts.StartActivityForResult(),
- new ActivityResultCallback() {
- @Override
- public void onActivityResult(ActivityResult result) {
- if (result.getResultCode() != Activity.RESULT_OK) {
- if(result.getResultCode() == Activity.RESULT_CANCELED){
- Toast.makeText(MainActivity.this,"Import data canceled",Toast.LENGTH_SHORT).show();
- }
- return;
- }
- if (result.getData() == null || result.getData().getData() == null){
- Toast.makeText(MainActivity.this, "Fail to load data", Toast.LENGTH_SHORT).show();
- return;
- }
- //File
- Uri uri = result.getData().getData();
- progressBar.setVisibility(View.VISIBLE);
- loadingFileTxt.setVisibility(View.VISIBLE);
- new Thread(() -> {
- String Data = FileSystem.LoadDataFromFile(MainActivity.this, uri);
-
- if (Data == null) {
- Log.d(TAG, "onActivityResult: null data");
- return;
- }
- handler.post(() -> {
- progressBar.setVisibility(View.GONE);
- loadingFileTxt.setVisibility(View.GONE);
- LoadData(Data);
- });
-
- }).start();
+ int setColor(int resid){
+ TypedValue typedValue = new TypedValue();
+ getTheme().resolveAttribute(resid, typedValue, true);
+ return typedValue.data;
+ }
- }
- });
+ void fileTooLargeException(){
+ postMessageException("File is too large");
+ }
+ void fileNotLoadedException(){
+ postMessageException("Fail to load file!");
+ }
+ void creatingListException(){
+ postMessageException("Fail to create list!");
+ }
+ void postMessageException(String msg){
+ handler.post(() -> {
+ Toast.makeText(MainActivity.this,msg, Toast.LENGTH_SHORT).show();
+ loadingFinished(false);
+ });
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sjapps/jsonlist/functions.java b/app/src/main/java/com/sjapps/jsonlist/functions.java
new file mode 100644
index 0000000..b40b0ec
--- /dev/null
+++ b/app/src/main/java/com/sjapps/jsonlist/functions.java
@@ -0,0 +1,41 @@
+package com.sjapps.jsonlist;
+
+import android.content.Context;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.AnimRes;
+import androidx.annotation.NonNull;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+public class functions {
+ public static String timeFormat(Calendar c){
+ if (c == null)
+ return "N/A";
+ SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, MMM d h:mm:ss a, yyyy");
+ return dateFormat.format(c.getTime());
+ }
+
+ public static String timeFormatShort(Calendar c){
+ if (c == null)
+ return "N/A";
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
+ return dateFormat.format(c.getTime());
+ }
+
+ public static void setAnimation(Context context, @NonNull View view, @AnimRes int animationRes) {
+ setAnimation(context,view,animationRes,null);
+ }
+
+ public static void setAnimation(Context context, @NonNull View view, @AnimRes int animationRes, Interpolator interpolator) {
+ Animation animation = AnimationUtils.loadAnimation(context, animationRes);
+ if (interpolator != null)
+ animation.setInterpolator(interpolator);
+ view.startAnimation(animation);
+ }
+
+}
diff --git a/app/src/main/java/com/sjapps/jsonlist/java/JsonData.java b/app/src/main/java/com/sjapps/jsonlist/java/JsonData.java
new file mode 100644
index 0000000..1c6b616
--- /dev/null
+++ b/app/src/main/java/com/sjapps/jsonlist/java/JsonData.java
@@ -0,0 +1,97 @@
+package com.sjapps.jsonlist.java;
+
+import java.util.ArrayList;
+import java.util.Stack;
+
+public class JsonData {
+ String path = "";
+ ArrayList rootList = new ArrayList<>();
+ Stack previousPosStack = new Stack<>();
+ String rawData = "";
+
+ int previousPos = -1;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public ArrayList getRootList() {
+ return rootList;
+ }
+
+ public void setRootList(ArrayList rootList) {
+ this.rootList = rootList;
+ }
+
+ public void setRawData(String data) {
+ this.rawData = data;
+ }
+
+ public String getRawData() {
+ return rawData;
+ }
+
+ public boolean isEmptyPath(){
+ return path.equals("");
+ }
+ public void clearPath(){
+ path = "";
+ }
+ public String[] splitPath(){
+ return path.split("///");
+ }
+ public static String[] splitPath(String path){
+ return path.split("///");
+ }
+
+ public boolean isRootListNull(){
+ return rootList == null;
+ }
+
+ public void goBack(){
+
+ if (!previousPosStack.isEmpty())
+ previousPos = previousPosStack.pop();
+
+ String[] pathStrings = splitPath();
+ clearPath();
+ for (int i = 0; i < pathStrings.length-1; i++) {
+ setPath(path.concat((isEmptyPath()?"":"///") + pathStrings[i]));
+ }
+
+ }
+
+ public void addPreviousPos(int pos){
+ previousPosStack.push(pos);
+ }
+
+ public int getPreviousPos(){
+ return previousPos;
+ }
+
+ public void clearPreviousPos(){
+ previousPosStack.clear();
+ }
+
+ public static String getPathFormat(String path){
+ String[] pathStrings = splitPath(path);
+ StringBuilder builder = new StringBuilder();
+ builder.append(pathStrings.length > 3 ? "..." : pathStrings[0]);
+
+ for (int i = pathStrings.length > 3? pathStrings.length-3 : 1; i < pathStrings.length; i++) {
+ builder.append("/").append(getName(pathStrings[i]));
+ }
+
+ return builder.toString();
+ }
+
+ public static String getName(String str){
+ if (str.startsWith("{") && str.contains("}") && str.substring(1, str.indexOf("}")).matches("^[0-9]+"))
+ return str.substring(str.indexOf("}")+1);
+ return str;
+ }
+}
diff --git a/app/src/main/java/com/sjapps/jsonlist/java/JsonFunctions.java b/app/src/main/java/com/sjapps/jsonlist/java/JsonFunctions.java
new file mode 100644
index 0000000..285f5af
--- /dev/null
+++ b/app/src/main/java/com/sjapps/jsonlist/java/JsonFunctions.java
@@ -0,0 +1,178 @@
+package com.sjapps.jsonlist.java;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+public class JsonFunctions {
+
+ public static ArrayList getJsonArrayRoot(JsonArray array) {
+ ArrayList mainList = new ArrayList<>();
+ ListItem item = new ListItem();
+ setArrayName(array,item);
+ item.setIsArray(true);
+ item.setListObjects(getJsonArray(array));
+ mainList.add(item);
+ return mainList;
+ }
+
+ public static ArrayList> getJsonArray(JsonArray array) {
+ ArrayList> ArrList = new ArrayList<>();
+ for (int i = 0; i < array.size(); i++) {
+ if (array.get(i) instanceof JsonObject) {
+ ArrayList ListOfItems = getJsonObject((JsonObject) array.get(i));
+ ArrList.add(ListOfItems);
+ continue;
+ }
+ if (array.get(i) instanceof JsonArray){
+
+ ArrayList> ListOfItems = getJsonArray((JsonArray) array.get(i));
+
+ ArrayList itemsInList = new ArrayList<>();
+ ListItem arrItem = new ListItem();
+
+ setArrayName((JsonArray) array.get(i),arrItem);
+ arrItem.setIsArray(true);
+ arrItem.setListObjects(ListOfItems);
+
+ itemsInList.add(arrItem);
+ ArrList.add(itemsInList);
+ continue;
+ }
+ ListItem item = new ListItem();
+ item.setValue(getStringFromJson(array.get(i).toString()));
+ ArrayList items = new ArrayList<>();
+ items.add(item);
+ ArrList.add(items);
+ }
+ return ArrList;
+ }
+
+ static boolean isArrayOfObjects(JsonArray array) {
+ for (int i = 0; i < array.size(); i++) {
+ if (!(array.get(i) instanceof JsonObject)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static boolean isArrayOfArray(JsonArray array) {
+ for (int i = 0; i < array.size(); i++) {
+ if (!(array.get(i) instanceof JsonArray)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static ArrayList getJsonObject(JsonObject obj) {
+ ArrayList mainList = new ArrayList<>();
+ Set keys = obj.keySet();
+ Object[] keysArray = keys.toArray();
+
+ for (Object o : keysArray) {
+ ListItem item = new ListItem();
+ item.setName(o.toString());
+ setItem(obj,o,item);
+ mainList.add(item);
+ }
+ return mainList;
+ }
+
+ private static void setArrayName(JsonArray array, ListItem item){
+ if(isArrayOfObjects(array)) {
+ item.setName("Objects Array");
+ return;
+ }
+ if (isArrayOfArray(array)){
+ item.setName("Array");
+ return;
+ }
+ item.setName("Array items");
+ }
+ private static String getStringFromJson(String value){
+ return value.startsWith("\"") && value.endsWith("\"") ? value.substring(1,value.length()-1) : value;
+ }
+
+ private static void setItem(JsonObject obj, Object o, ListItem item){
+ if (obj.get(o.toString()) instanceof JsonObject) {
+ item.setIsObject(true);
+ ArrayList objList = getJsonObject((JsonObject) obj.get(o.toString()));
+ item.setObjects(objList);
+ return;
+ }
+ if (obj.get(o.toString()) instanceof JsonArray) {
+ JsonArray array = (JsonArray) obj.get(o.toString());
+
+ item.setIsArray(true);
+ item.setListObjects(getJsonArray(array));
+ return;
+ }
+ item.setValue(getStringFromJson(obj.get(o.toString()).toString()));
+ }
+
+ static ArrayList getArrayList(ArrayList> list) {
+ ArrayList newList = new ArrayList<>();
+ for (ArrayList lists : list) {
+ setId(lists, list.indexOf(lists));
+ newList.addAll(lists);
+ newList.add(new ListItem().Space());
+ }
+ return newList;
+ }
+
+ private static void setId(ArrayList lists, int id) {
+
+ for (ListItem listItem : lists) {
+ listItem.setId(id);
+ }
+ }
+
+ public static ArrayList getListFromPath(String path, ArrayList rootList) {
+
+
+ String[] pathStrings = path.split("///");
+
+ ArrayList list = rootList;
+
+ for (String pathString : pathStrings) {
+
+ int id = -1;
+
+ if (pathString.startsWith("{") && pathString.contains("}") && pathString.substring(1, pathString.indexOf("}")).matches("^[0-9]+")) {
+ id = Integer.parseInt(pathString.substring(1, pathString.indexOf("}")));
+ }
+
+ for (ListItem item : list) {
+ if (item.getName() == null || !item.getName().equals(id != -1 ? pathString.substring(pathString.indexOf("}") + 1) : pathString))
+ continue;
+
+ if (id != -1 && item.getId() != id)
+ continue;
+
+ if (item.isArray()) {
+ list = getArrayList(item.getListObjects());
+ break;
+ }
+ list = list.get(list.indexOf(item)).getObjects();
+ if (list == null)
+ list = new ArrayList<>();
+ break;
+ }
+ }
+ return list;
+
+ }
+
+ public static String getAsPrettyPrint(String data){
+ JsonElement json = JsonParser.parseString(data);
+ Gson gson = new Gson().newBuilder().setPrettyPrinting().create();
+ return gson.toJson(json);
+ }
+}
diff --git a/app/src/main/java/com/sjapps/jsonlist/ListItem.java b/app/src/main/java/com/sjapps/jsonlist/java/ListItem.java
similarity index 51%
rename from app/src/main/java/com/sjapps/jsonlist/ListItem.java
rename to app/src/main/java/com/sjapps/jsonlist/java/ListItem.java
index aea15dc..efca171 100644
--- a/app/src/main/java/com/sjapps/jsonlist/ListItem.java
+++ b/app/src/main/java/com/sjapps/jsonlist/java/ListItem.java
@@ -1,4 +1,4 @@
-package com.sjapps.jsonlist;
+package com.sjapps.jsonlist.java;
import java.util.ArrayList;
@@ -6,12 +6,12 @@ public class ListItem {
private String Name;
private String Value;
- private boolean isArrayOfObjects;
private boolean isArray;
private boolean isObject;
private boolean isSpace;
private ArrayList Objects;
private ArrayList> ListObjects;
+ private int id = -1;
public ListItem(){
}
@@ -33,14 +33,6 @@ public void setValue(String value) {
Value = value;
}
- public boolean isArrayOfObjects() {
- return isArrayOfObjects;
- }
-
- public void setIsArrayOfObjects(boolean array) {
- isArrayOfObjects = array;
- }
-
public boolean isArray() {
return isArray;
}
@@ -81,17 +73,25 @@ public void setListObjects(ArrayList> listObjects) {
ListObjects = listObjects;
}
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
@Override
public String toString() {
- return "ListItem{" +
- "Name='" + Name + '\'' +
- ", Value='" + Value + '\'' +
- ", isArrayOfObjects=" + isArrayOfObjects +
- ", isArray=" + isArray +
- ", isObject=" + isObject +
- ", isSpace=" + isSpace +
- ", Objects=" + Objects +
- ", ListObjects=" + ListObjects +
+ return "{" +
+ "\"ID\":" + id +
+ ",\"Name\":" +(Name!=null && !Name.startsWith("\"")?"\"":"") + Name + (Name!=null && !Name.startsWith("\"")?"\"":"") +
+ ", \"Value\":" + (Value!=null && !Value.startsWith("\"")?"\"":"") + Value + (Value!=null && !Value.startsWith("\"")?"\"":"") +
+ ", \"isArray\":" + isArray +
+ ", \"isObject\":" + isObject +
+ ", \"isSpace\":" + isSpace +
+ ", \"Objects\":" + Objects +
+ ", \"ListObjects\":" + ListObjects +
'}';
}
@@ -99,4 +99,17 @@ public ListItem Space() {
setIsSpace(true);
return this;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ListItem)) return false;
+ ListItem item = (ListItem) o;
+ return isArray() == item.isArray() && isObject() == item.isObject() && isSpace() == item.isSpace() && java.util.Objects.equals(getName(), item.getName()) && java.util.Objects.equals(getValue(), item.getValue()) && java.util.Objects.equals(getObjects(), item.getObjects()) && java.util.Objects.equals(getListObjects(), item.getListObjects());
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(getName(), getValue(), isArray(), isObject(), isSpace(), getObjects(), getListObjects());
+ }
}
diff --git a/app/src/main/java/com/sjapps/logs/CrashLogs.java b/app/src/main/java/com/sjapps/logs/CrashLogs.java
new file mode 100644
index 0000000..3d83f56
--- /dev/null
+++ b/app/src/main/java/com/sjapps/logs/CrashLogs.java
@@ -0,0 +1,27 @@
+package com.sjapps.logs;
+
+import java.util.ArrayList;
+
+public class CrashLogs {
+
+ ArrayList logs = new ArrayList<>();
+
+ public void addLog(String s){
+ logs.add(s);
+ }
+
+ public ArrayList getLogs() {
+ return logs;
+ }
+
+ public void setLogs(ArrayList logs) {
+ this.logs = logs;
+ }
+
+ @Override
+ public String toString() {
+ return "RuntimeExceptions{" +
+ "logs=" + logs +
+ '}';
+ }
+}
diff --git a/app/src/main/java/com/sjapps/logs/CustomExceptionHandler.java b/app/src/main/java/com/sjapps/logs/CustomExceptionHandler.java
new file mode 100644
index 0000000..88da5e5
--- /dev/null
+++ b/app/src/main/java/com/sjapps/logs/CustomExceptionHandler.java
@@ -0,0 +1,58 @@
+package com.sjapps.logs;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.google.gson.Gson;
+import com.sjapps.jsonlist.AppState;
+import com.sjapps.jsonlist.FileSystem;
+import com.sjapps.jsonlist.functions;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Calendar;
+
+
+public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
+
+ private Thread.UncaughtExceptionHandler defaultUEH;
+ Context context;
+ Calendar calendar;
+
+
+ public CustomExceptionHandler(Context context) {
+ defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
+ this.context = context;
+
+ }
+
+ @Override
+ public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
+
+ AppState state = FileSystem.loadStateData(context);
+
+ if (!state.hasCrashLogs())
+ state.setHasCrashLogs(true);
+
+ state.setHasNewCrash(true);
+ FileSystem.SaveState(context,new Gson().toJson(state));
+
+ final Writer result = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(result);
+ e.printStackTrace(printWriter);
+ String stacktrace = result.toString();
+ printWriter.close();
+ calendar = Calendar.getInstance();
+ String log = "";
+ log += "\n\n-- " + functions.timeFormat(calendar) + " -- \n";
+ log += stacktrace;
+
+ CrashLogs exceptions = FileSystem.loadLogData(context);
+
+ exceptions.addLog(log);
+ FileSystem.SaveLog(context,new Gson().toJson(exceptions));
+ defaultUEH.uncaughtException(t, e);
+ }
+}
diff --git a/app/src/main/java/com/sjapps/logs/LogActivity.java b/app/src/main/java/com/sjapps/logs/LogActivity.java
new file mode 100644
index 0000000..10bad74
--- /dev/null
+++ b/app/src/main/java/com/sjapps/logs/LogActivity.java
@@ -0,0 +1,194 @@
+package com.sjapps.logs;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.FileProvider;
+
+import com.google.gson.Gson;
+import com.sjapps.jsonlist.AppState;
+import com.sjapps.jsonlist.FileSystem;
+import com.sjapps.jsonlist.R;
+import com.sjapps.jsonlist.functions;
+import com.sjapps.library.customdialog.BasicDialog;
+import com.sjapps.library.customdialog.ListDialog;
+import com.sjapps.library.customdialog.MessageDialog;
+
+
+import java.util.Calendar;
+
+public class LogActivity extends AppCompatActivity {
+
+ TextView logTxt;
+ String exportFileName = "logFile.txt";
+ int numberOfLogs = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_log);
+
+ logTxt = findViewById(R.id.logTxt);
+ update();
+
+ AppState state = FileSystem.loadStateData(this);
+
+ if (state.hasNewCrash()){
+ state.setHasNewCrash(false);
+ FileSystem.SaveState(this,new Gson().toJson(state));
+ }
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ FileSystem.deleteTempFile(this,exportFileName);
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+
+
+ public void Back(View view) {
+ finish();
+ }
+
+ public void deleteLog(View view) {
+ BasicDialog dialog = new BasicDialog();
+ dialog.Delete(this,true)
+ .setTitle("Delete logs?")
+ .onButtonClick(() ->{
+ dialog.dismiss();
+ FileSystem.SaveLog(this,new Gson().toJson(new CrashLogs()));
+ update();
+ })
+ .show();
+ }
+
+ private void update() {
+
+ CrashLogs logs = FileSystem.loadLogData(this);
+
+ AppState state = FileSystem.loadStateData(this);
+
+ if (state.hasCrashLogs() && logs.getLogs().isEmpty()){
+ state.setHasCrashLogs(false);
+ FileSystem.SaveState(this,new Gson().toJson(state));
+ }
+ StringBuilder log = new StringBuilder();
+
+ numberOfLogs = logs.getLogs().size();
+
+ log.append(getDeviceInfo());
+
+ for (String s : logs.getLogs()){
+ log.append(s);
+ }
+ logTxt.setText(log.toString());
+ }
+
+ private String getDeviceInfo(){
+
+ String s = "";
+ try {
+ PackageInfo pInfo = getPackageManager().getPackageInfo(
+ getPackageName(), PackageManager.GET_META_DATA);
+ s += "\n App Version Name: " + pInfo.versionName;
+ s += "\n App Version Code: " + pInfo.versionCode;
+ s += "\n";
+ } catch (PackageManager.NameNotFoundException ignored) {}
+ s += "\n OS Version: " + System.getProperty("os.version") + " ("
+ + Build.VERSION.INCREMENTAL + ")";
+ s += "\n OS API Level: " + Build.VERSION.SDK_INT;
+ s += "\n Device: " + Build.DEVICE;
+ s += "\n Model (and Product): " + Build.MODEL + " (" + Build.PRODUCT + ")";
+ s += "\n Manufacturer: " + Build.MANUFACTURER;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ s += "\n screenWidth: " + getWindowManager().getCurrentWindowMetrics().getBounds().width();
+ s += "\n screenHeight: " + getWindowManager().getCurrentWindowMetrics().getBounds().height();
+ }else {
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ s += "\n screenWidth: " + metrics.widthPixels;
+ s += "\n screenHeight: " + metrics.heightPixels;
+ }
+
+ s += "\n";
+
+ return s;
+ }
+
+ public void shareLog(View view) {
+
+ if (logTxt.getText().toString().equals("")) {
+ Toast.makeText(this, "File is empty", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ String[] options = {"Copy logs to clipboard","Share logs"};
+
+ ListDialog dialog = new ListDialog();
+ dialog.Builder(this,true)
+ .setTitle("Chose action")
+ .setItems(options,(position, value) -> {
+ dialog.dismiss();
+ switch (position){
+ case 0: copyToClipboard();
+ break;
+ case 1: share();
+ break;
+ }
+ });
+
+ MessageDialog warningDialog = new MessageDialog();
+ warningDialog.ErrorDialogBuilder(this,true)
+ .setTitle("Warning!")
+ .setMessage("This reports may include personal information. Check before sharing to anyone")
+ .show();
+ warningDialog.dialog.setOnDismissListener(dialogInterface -> {
+ warningDialog.dismiss();
+ dialog.show();
+ });
+ }
+
+ private void share() {
+
+ exportFileName = "logFile " + functions.timeFormatShort(Calendar.getInstance()) + ".txt";
+
+ String appName = getResources().getString(R.string.app_name);
+
+ Intent intentShare = new Intent(Intent.ACTION_SEND);
+ intentShare.setType("text/plain");
+ intentShare.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".logs.provider",
+ FileSystem.createTempFile(this,logTxt.getText().toString(),exportFileName)));
+ intentShare.putExtra(Intent.EXTRA_SUBJECT, appName + " Crash logs");
+ intentShare.putExtra(Intent.EXTRA_TEXT, numberOfLogs + " Crash log" + (numberOfLogs>1?"s":"") + " for " + appName);
+
+ startActivity(Intent.createChooser(intentShare,"Share file"));
+ }
+
+ private void copyToClipboard() {
+
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clipData = ClipData.newPlainText("log",logTxt.getText().toString());
+ clipboard.setPrimaryClip(clipData);
+ Toast.makeText(this, "logs is copied to clipboard", Toast.LENGTH_SHORT).show();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/anim/button_pop.xml b/app/src/main/res/anim/button_pop.xml
new file mode 100644
index 0000000..05b7596
--- /dev/null
+++ b/app/src/main/res/anim/button_pop.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/anim/button_prev.xml b/app/src/main/res/anim/button_prev.xml
new file mode 100644
index 0000000..5196dcb
--- /dev/null
+++ b/app/src/main/res/anim/button_prev.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/anim/scale_in.xml b/app/src/main/res/anim/scale_in.xml
new file mode 100644
index 0000000..7580a87
--- /dev/null
+++ b/app/src/main/res/anim/scale_in.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/scale_in2.xml b/app/src/main/res/anim/scale_in2.xml
new file mode 100644
index 0000000..11d602b
--- /dev/null
+++ b/app/src/main/res/anim/scale_in2.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/scale_in_file_img.xml b/app/src/main/res/anim/scale_in_file_img.xml
new file mode 100644
index 0000000..03f955c
--- /dev/null
+++ b/app/src/main/res/anim/scale_in_file_img.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/anim/scale_out.xml b/app/src/main/res/anim/scale_out.xml
new file mode 100644
index 0000000..b3440f4
--- /dev/null
+++ b/app/src/main/res/anim/scale_out.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_bottom_in.xml b/app/src/main/res/anim/slide_bottom_in.xml
new file mode 100644
index 0000000..99edc3b
--- /dev/null
+++ b/app/src/main/res/anim/slide_bottom_in.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_bottom_out.xml b/app/src/main/res/anim/slide_bottom_out.xml
new file mode 100644
index 0000000..8ee0230
--- /dev/null
+++ b/app/src/main/res/anim/slide_bottom_out.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_right_in.xml b/app/src/main/res/anim/slide_right_in.xml
new file mode 100644
index 0000000..8a34a20
--- /dev/null
+++ b/app/src/main/res/anim/slide_right_in.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_right_out.xml b/app/src/main/res/anim/slide_right_out.xml
new file mode 100644
index 0000000..6e32903
--- /dev/null
+++ b/app/src/main/res/anim/slide_right_out.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_transparent.xml b/app/src/main/res/drawable/background_transparent.xml
new file mode 100644
index 0000000..79dbd43
--- /dev/null
+++ b/app/src/main/res/drawable/background_transparent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/github_logo.xml b/app/src/main/res/drawable/github_logo.xml
new file mode 100644
index 0000000..30350d2
--- /dev/null
+++ b/app/src/main/res/drawable/github_logo.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
index 4d4019e..d2ad538 100644
--- a/app/src/main/res/drawable/ic_back.xml
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -6,5 +6,5 @@
android:tint="?attr/colorControlNormal">
+ android:pathData="M15.5 7.5c.5-.5.3-1.2 0-1.5s-1-.5-1.5 0L9 11C8.5 11.5 8.5 11.9 8.5 12S8.5 12.5 9 13L14 18c.5.5 1.2.3 1.5 0s.5-1 0-1.5L12 13C11.5 12.5 11.5 12.1 11.5 12S11.5 11.5 12 11z"/>
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
index aea55ad..e483926 100644
--- a/app/src/main/res/drawable/ic_close.xml
+++ b/app/src/main/res/drawable/ic_close.xml
@@ -6,5 +6,5 @@
android:tint="?attr/colorControlNormal">
+ android:pathData="M18.295 7.115C18.6475 6.7625 18.6475 6.0575 18.295 5.705S17.2375 5.3525 16.885 5.705L12.7 9.885c-.3525.3525-.5.4-.705.4s-.465-.165-.7-.4L7.115 5.705C6.7625 5.3525 6.0575 5.3525 5.705 5.705S5.3525 6.7625 5.705 7.115L9.885 11.295c.3525.3525.4.5.4.705S10.2375 12.3525 9.885 12.705L5.7 16.885C5.3475 17.2375 5.3525 17.9425 5.705 18.295S6.7625 18.6475 7.115 18.295L11.295 14.115c.3525-.3525.5-.4.705-.4S12.3525 13.7625 12.705 14.115L16.885 18.295C17.2375 18.6475 17.9425 18.6475 18.295 18.295S18.6475 17.2375 18.295 16.885L14.115 12.705c-.3525-.3525-.4-.5-.4-.705S13.7625 11.6475 14.115 11.295z"/>
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..41fa9d6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml
index e0409ca..a917303 100644
--- a/app/src/main/res/drawable/ic_menu.xml
+++ b/app/src/main/res/drawable/ic_menu.xml
@@ -6,5 +6,5 @@
android:tint="?attr/colorControlNormal">
+ android:pathData="M4,18h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,16c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM4,13h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM3,7c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,6c-0.55,0 -1,0.45 -1,1z"/>
diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml
new file mode 100644
index 0000000..d013943
--- /dev/null
+++ b/app/src/main/res/drawable/ic_share.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_splitscreen.xml b/app/src/main/res/drawable/ic_splitscreen.xml
new file mode 100644
index 0000000..5bf0a95
--- /dev/null
+++ b/app/src/main/res/drawable/ic_splitscreen.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/menu_with_dot.xml b/app/src/main/res/drawable/menu_with_dot.xml
new file mode 100644
index 0000000..bef5432
--- /dev/null
+++ b/app/src/main/res/drawable/menu_with_dot.xml
@@ -0,0 +1,21 @@
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ripple_red.xml b/app/src/main/res/drawable/ripple_red.xml
new file mode 100644
index 0000000..5f5a63b
--- /dev/null
+++ b/app/src/main/res/drawable/ripple_red.xml
@@ -0,0 +1,14 @@
+
+
+ -
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/split_view_bg.xml b/app/src/main/res/drawable/split_view_bg.xml
new file mode 100644
index 0000000..9677f9a
--- /dev/null
+++ b/app/src/main/res/drawable/split_view_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index 28d0504..58e274b 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -72,7 +72,7 @@
android:layout_marginTop="20dp"
android:layout_marginBottom="5dp"
- android:text="Libraries"
+ android:text="Open source libraries"
android:textSize="25sp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index e78542e..1c6cdc9 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -28,16 +28,16 @@
+
+
-
+ android:weightSum="2"
+ android:orientation="vertical"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
- android:visibility="gone" />
-
diff --git a/app/src/main/res/layout/activity_menu.xml b/app/src/main/res/layout/activity_menu.xml
index 2f7b157..3deff0a 100644
--- a/app/src/main/res/layout/activity_menu.xml
+++ b/app/src/main/res/layout/activity_menu.xml
@@ -39,7 +39,17 @@
android:paddingHorizontal="20dp"
android:background="@drawable/ripple_list2"
android:text="About"/>
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_layout2.xml b/app/src/main/res/layout/list_layout2.xml
index a50f756..b2414e7 100644
--- a/app/src/main/res/layout/list_layout2.xml
+++ b/app/src/main/res/layout/list_layout2.xml
@@ -21,12 +21,12 @@
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
- android:layout_marginBottom="10dp"
android:textStyle="bold"
android:textIsSelectable="true"
android:longClickable="true"
android:textColor="?colorOnSurfaceVariant"
android:autoLink="all"
+ android:visibility="visible"
android:text="Item"
android:textSize="25dp" />
@@ -36,7 +36,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/itemName"
android:layout_marginHorizontal="10dp"
- android:layout_marginBottom="10dp"
+ android:layout_marginVertical="10dp"
android:textIsSelectable="true"
android:longClickable="true"
android:textColor="?colorOnSurfaceVariant"
diff --git a/app/src/main/res/layout/space_layout.xml b/app/src/main/res/layout/space_layout.xml
index 4d18ece..6e00b5b 100644
--- a/app/src/main/res/layout/space_layout.xml
+++ b/app/src/main/res/layout/space_layout.xml
@@ -1,6 +1,6 @@
+ android:layout_height="10dp">
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 4ae7d12..a4781b4 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 4ae7d12..a4781b4 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome_foreground.png
new file mode 100644
index 0000000..fe200d1
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome_foreground.png
new file mode 100644
index 0000000..58acd2d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome_foreground.png
new file mode 100644
index 0000000..60e3ba6
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome_foreground.png
new file mode 100644
index 0000000..ff68b6c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome_foreground.png
new file mode 100644
index 0000000..4a1ef8b
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome_foreground.png differ
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 35212c3..d2ed08c 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -31,7 +31,12 @@
- @style/ButtonColor
- ?android:windowBackgroundFallback
-
+ - ?android:windowBackgroundFallback
+ - shortEdges
+ - #000000
+ - #000000
+ - @style/SnackbarStyle
+ - @style/SnackbarStyle
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_launcher_monochrome_background.xml b/app/src/main/res/values/ic_launcher_monochrome_background.xml
new file mode 100644
index 0000000..f79b5ab
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_monochrome_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5d8256b..3cd16e9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,4 +2,7 @@
Json List
Open file
Back
+ Drop JSON file here
+ This is not JSON file
+ Only one file is allowed!
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 18174fe..65b67e8 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -30,8 +30,14 @@
- @color/md_theme_light_primaryInverse
- ?android:windowBackgroundFallback
- - true
+ - ?android:windowBackgroundFallback
+ - shortEdges
+ - #ffffff
+ - #ffffff
+ - true
+ - @style/SnackbarStyle
+ - @style/SnackbarStyle
- @style/ButtonColor
-
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml-v25/shortcuts.xml
similarity index 100%
rename from app/src/main/res/xml/shortcuts.xml
rename to app/src/main/res/xml-v25/shortcuts.xml
diff --git a/app/src/main/res/xml/file_path.xml b/app/src/main/res/xml/file_path.xml
new file mode 100644
index 0000000..24b6e4e
--- /dev/null
+++ b/app/src/main/res/xml/file_path.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/GetArrayListTest.java b/app/src/test/java/com/sjapps/jsonlist/java/GetArrayListTest.java
new file mode 100644
index 0000000..1939713
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/GetArrayListTest.java
@@ -0,0 +1,56 @@
+package com.sjapps.jsonlist.java;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GetArrayListTest {
+
+ @Test
+ public void testGetArrayList() {
+ ArrayList> inputList = new ArrayList<>();
+
+ ArrayList list1 = new ArrayList<>();
+ ListItem item1 = new ListItem();
+ item1.setName("Item 1");
+ item1.setValue("Value 1");
+ list1.add(item1);
+
+ ArrayList list2 = new ArrayList<>();
+ ListItem item2 = new ListItem();
+ item2.setName("Item 2");
+ item2.setValue("Value 2");
+ list2.add(item2);
+
+ inputList.add(list1);
+ inputList.add(list2);
+
+ ArrayList result = JsonFunctions.getArrayList(inputList);
+
+ assertEquals(4, result.size());
+ assertEquals("Item 1", result.get(0).getName());
+ assertEquals("Value 1", result.get(0).getValue());
+ assertFalse(result.get(0).isSpace());
+ assertEquals("Item 2", result.get(2).getName());
+ assertEquals("Value 2", result.get(2).getValue());
+ assertFalse(result.get(2).isSpace());
+ assertEquals(0, result.get(0).getId());
+ assertEquals(1, result.get(2).getId());
+ }
+
+ @Test
+ public void testGetArrayListEmpty() {
+
+ ArrayList> lists = new ArrayList<>();
+ ArrayList result = JsonFunctions.getArrayList(lists);
+
+ assertNotNull(result);
+ assertEquals(0,result.size());
+ }
+
+}
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/GetJsonArrayRootTest.java b/app/src/test/java/com/sjapps/jsonlist/java/GetJsonArrayRootTest.java
new file mode 100644
index 0000000..77b348b
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/GetJsonArrayRootTest.java
@@ -0,0 +1,109 @@
+package com.sjapps.jsonlist.java;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.sjapps.jsonlist.FileSystem;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class GetJsonArrayRootTest {
+
+ @Test
+ public void getJsonArrayRootTest(){
+ String data = "[ {data2:\"123\"} ]";
+ JsonElement element = JsonParser.parseString(data);
+
+ JsonArray array = FileSystem.loadDataToJsonArray(element);
+
+ ArrayList itemsArr = JsonFunctions.getJsonArrayRoot(array);
+ ArrayList expectedArr = new ArrayList<>();
+
+ ListItem root = new ListItem();
+ root.setName("Objects Array");
+ root.setIsArray(true);
+
+ ArrayList listItems = new ArrayList<>();
+
+ ListItem OLItem = new ListItem();
+
+ OLItem.setName("data2");
+ OLItem.setValue("123");
+ listItems.add(OLItem);
+
+ ArrayList> arrayListArrayList = new ArrayList<>();
+ arrayListArrayList.add(listItems);
+ root.setListObjects(arrayListArrayList);
+ expectedArr.add(root);
+ assertEquals(expectedArr,itemsArr);
+
+ }
+ @Test
+ public void getJsonArrayRootTest2(){
+ String data = "[{\"data2\":123},1242,true,null]";
+ JsonElement element = JsonParser.parseString(data);
+
+ JsonArray array = FileSystem.loadDataToJsonArray(element);
+
+ ArrayList itemsArr = JsonFunctions.getJsonArrayRoot(array);
+
+
+ ArrayList expectedArr = new ArrayList<>();
+
+ ListItem root = new ListItem();
+ root.setName("Array items");
+ root.setIsArray(true);
+
+ ArrayList> arrayListArrayList = new ArrayList<>();
+
+ ArrayList items1 = new ArrayList<>();
+ ListItem item1 = new ListItem();
+ item1.setName("data2");
+ item1.setValue("123");
+ items1.add(item1);
+ arrayListArrayList.add(items1);
+
+ ArrayList items2 = new ArrayList<>();
+ ListItem item2 = new ListItem();
+ item2.setValue("1242");
+ items2.add(item2);
+ arrayListArrayList.add(items2);
+
+ ArrayList items3 = new ArrayList<>();
+ ListItem item3 = new ListItem();
+ item3.setValue("true");
+ items3.add(item3);
+ arrayListArrayList.add(items3);
+
+ ArrayList items4 = new ArrayList<>();
+ ListItem item4 = new ListItem();
+ item4.setValue("null");
+ items4.add(item4);
+ arrayListArrayList.add(items4);
+
+
+ root.setListObjects(arrayListArrayList);
+
+ expectedArr.add(root);
+ assertEquals(expectedArr,itemsArr);
+
+ }
+
+ @Test
+ public void testGetJsonArrayRootWithEmptyArray() {
+ JsonArray jsonArray = new JsonArray();
+ ArrayList result = JsonFunctions.getJsonArrayRoot(jsonArray);
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("Objects Array", result.get(0).getName());
+ assertTrue(result.get(0).isArray());
+ assertEquals(0, result.get(0).getListObjects().size());
+ }
+
+}
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/GetJsonObjectTest.java b/app/src/test/java/com/sjapps/jsonlist/java/GetJsonObjectTest.java
new file mode 100644
index 0000000..326acc9
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/GetJsonObjectTest.java
@@ -0,0 +1,215 @@
+package com.sjapps.jsonlist.java;
+
+import static junit.framework.TestCase.*;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class GetJsonObjectTest {
+
+ @Test
+ public void testGetJsonObjectWithNestedObject() {
+ JsonObject jsonObject = new JsonObject();
+ JsonObject nestedObject = new JsonObject();
+ nestedObject.addProperty("nestedKey", "nestedValue");
+ jsonObject.add("key1", nestedObject);
+
+ ArrayList result = JsonFunctions.getJsonObject(jsonObject);
+
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("key1", result.get(0).getName());
+ assertTrue(result.get(0).isObject());
+ assertEquals(1, result.get(0).getObjects().size());
+ assertEquals("nestedKey", result.get(0).getObjects().get(0).getName());
+ assertEquals("nestedValue", result.get(0).getObjects().get(0).getValue());
+ }
+
+ @Test
+ public void testGetJsonObjectWithNestedArray() {
+ JsonObject jsonObject = new JsonObject();
+ JsonArray jsonArray = new JsonArray();
+ jsonArray.add("value1");
+ jsonArray.add("value2");
+ jsonObject.add("key1", jsonArray);
+
+ ArrayList result = JsonFunctions.getJsonObject(jsonObject);
+
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("key1", result.get(0).getName());
+ assertTrue(result.get(0).isArray());
+ assertEquals("value1",result.get(0).getListObjects().get(0).get(0).getValue());
+ assertEquals("value2",result.get(0).getListObjects().get(1).get(0).getValue());
+
+ }
+
+
+ @Test
+ public void testGetJsonObjectFromString() {
+ String jsonString = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
+
+ JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class);
+
+ ArrayList result = JsonFunctions.getJsonObject(jsonObject);
+
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertEquals("key1", result.get(0).getName());
+ assertEquals("value1", result.get(0).getValue());
+ assertEquals("key2", result.get(1).getName());
+ assertEquals("value2", result.get(1).getValue());
+ }
+
+
+ @Test
+ public void getJsonObjectFromString() {
+ String data = "{'item1':123,'item2':'test','item3':true,'item4':null,'item5':'123'}";
+
+ JsonObject object = new Gson().fromJson(data, JsonObject.class);
+
+ ArrayList items = JsonFunctions.getJsonObject(object);
+
+ ArrayList expected = new ArrayList<>();
+
+ ListItem item1 = new ListItem();
+ ListItem item2 = new ListItem();
+ ListItem item3 = new ListItem();
+ ListItem item4 = new ListItem();
+ ListItem item5 = new ListItem();
+
+ item1.setName("item1");
+ item1.setValue("123");
+
+ item2.setName("item2");
+ item2.setValue("test");
+
+
+ item3.setName("item3");
+ item3.setValue("true");
+
+ item4.setName("item4");
+ item4.setValue("null");
+
+ item5.setName("item5");
+ item5.setValue("123");
+
+ expected.add(item1);
+ expected.add(item2);
+ expected.add(item3);
+ expected.add(item4);
+ expected.add(item5);
+
+
+ assertNotNull(items);
+ assertEquals(item1,items.get(0));
+ assertEquals(item2,items.get(1));
+ assertEquals(item3,items.get(2));
+ assertEquals(item4,items.get(3));
+ assertEquals(item5,items.get(4));
+ assertEquals(expected, items);
+
+ }
+
+ @Test
+ public void getJsonObjectFromStringObject() {
+ String data = "{'item1':'test','item2':{'item3':true,'item4':null}}";
+
+ JsonObject object = new Gson().fromJson(data, JsonObject.class);
+
+ ArrayList items = JsonFunctions.getJsonObject(object);
+
+ ArrayList expected = new ArrayList<>();
+
+ ListItem item1 = new ListItem();
+ ListItem item2 = new ListItem();
+
+ item1.setName("item1");
+ item1.setValue("test");
+
+ item2.setName("item2");
+ item2.setIsObject(true);
+
+ ArrayList objs = new ArrayList<>();
+ ListItem item3 = new ListItem();
+ item3.setName("item3");
+ item3.setValue("true");
+ objs.add(item3);
+
+ ListItem item4 = new ListItem();
+ item4.setName("item4");
+ item4.setValue("null");
+ objs.add(item4);
+
+ item2.setObjects(objs);
+
+ expected.add(item1);
+ expected.add(item2);
+
+ assertNotNull(items);
+ assertEquals(item1,items.get(0));
+ assertEquals(item2,items.get(1));
+ assertTrue(items.get(1).isObject());
+ assertEquals(objs,items.get(1).getObjects());
+ assertEquals(item3,items.get(1).getObjects().get(0));
+ assertEquals(item4,items.get(1).getObjects().get(1));
+ assertEquals(expected, items);
+
+ }
+
+ @Test
+ public void getJsonObjectFromStringArray() {
+ String data = "{'item1':'test','item2':[{'item3':true},{'item4':null}]}";
+
+ JsonObject object = new Gson().fromJson(data, JsonObject.class);
+
+ ArrayList items = JsonFunctions.getJsonObject(object);
+
+ ArrayList expected = new ArrayList<>();
+
+ ListItem item1 = new ListItem();
+ ListItem item2 = new ListItem();
+
+ item1.setName("item1");
+ item1.setValue("test");
+
+ item2.setName("item2");
+ item2.setIsArray(true);
+
+ ArrayList> objs = new ArrayList<>();
+ ArrayList items1 = new ArrayList<>();
+ ListItem item3 = new ListItem();
+ item3.setName("item3");
+ item3.setValue("true");
+ items1.add(item3);
+ objs.add(items1);
+
+
+ ArrayList items2 = new ArrayList<>();
+ ListItem item4 = new ListItem();
+ item4.setName("item4");
+ item4.setValue("null");
+ items2.add(item4);
+ objs.add(items2);
+
+ item2.setListObjects(objs);
+
+ expected.add(item1);
+ expected.add(item2);
+
+ assertNotNull(items);
+ assertEquals(item1,items.get(0));
+ assertEquals(item2,items.get(1));
+ assertTrue(items.get(1).isArray());
+ assertEquals(items1,items.get(1).getListObjects().get(0));
+ assertEquals(items2,items.get(1).getListObjects().get(1));
+ assertEquals(expected, items);
+
+ }
+}
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/GetListFromPathTest.java b/app/src/test/java/com/sjapps/jsonlist/java/GetListFromPathTest.java
new file mode 100644
index 0000000..9637aac
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/GetListFromPathTest.java
@@ -0,0 +1,168 @@
+package com.sjapps.jsonlist.java;
+
+import static junit.framework.TestCase.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class GetListFromPathTest {
+
+ @Test
+ public void testGetListFromPathWithNestedStructureObject() {
+ ArrayList rootList = new ArrayList<>();
+ ListItem item1 = new ListItem();
+ item1.setName("Item1");
+ rootList.add(item1);
+ item1.setIsObject(true);
+
+ ArrayList nestedList = new ArrayList<>();
+ ListItem item2 = new ListItem();
+ item2.setName("Item2");
+
+ ArrayList nestedNestedList = new ArrayList<>();
+
+ ListItem item3 = new ListItem();
+ item3.setName("Item3");
+ item3.setValue("test");
+
+ ListItem item4 = new ListItem();
+ item4.setName("Item4");
+ item4.setValue("123");
+
+ nestedNestedList.add(item3);
+ nestedNestedList.add(item4);
+
+ item2.setObjects(nestedNestedList);
+ item2.setIsObject(true);
+
+
+ nestedList.add(item2);
+ item1.setObjects(nestedList);
+
+ ArrayList result = JsonFunctions.getListFromPath("Item1", rootList);
+
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("Item2", result.get(0).getName());
+
+ result = JsonFunctions.getListFromPath("Item1///Item2", rootList);
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertEquals("Item3", result.get(0).getName());
+ assertEquals("test", result.get(0).getValue());
+ assertEquals("Item4", result.get(1).getName());
+ assertEquals("123", result.get(1).getValue());
+ }
+
+ @Test
+ public void testGetListFromPathWithNestedStructureArrayOfObjects() {
+ ArrayList rootList = new ArrayList<>();
+ ListItem item1 = new ListItem();
+ item1.setName("Item1");
+ rootList.add(item1);
+ item1.setIsArray(true);
+
+ ArrayList> nestedListList = new ArrayList<>();
+ ArrayList nestedList1 = new ArrayList<>();
+ ListItem item2 = new ListItem();
+ item2.setName("Item2");
+ item2.setValue("test");
+ nestedList1.add(item2);
+
+ ArrayList nestedList2 = new ArrayList<>();
+ ListItem item3 = new ListItem();
+ item3.setName("Item3");
+
+ ArrayList innerListItem3 = new ArrayList<>();
+
+ ListItem item = new ListItem();
+ item.setName("item");
+ item.setValue("val");
+
+ innerListItem3.add(item);
+ item3.setObjects(innerListItem3);
+ nestedList2.add(item3);
+
+ nestedListList.add(nestedList1);
+ nestedListList.add(nestedList2);
+
+ item1.setListObjects(nestedListList);
+
+ ArrayList result = JsonFunctions.getListFromPath("", rootList);
+
+ assertEquals(1,result.size());
+ assertEquals("Item1",result.get(0).getName());
+ assertEquals("Item2",result.get(0).getListObjects().get(0).get(0).getName());
+
+ result = JsonFunctions.getListFromPath("Item1", rootList);
+
+ assertNotNull(result);
+ assertEquals(4, result.size());
+ assertEquals("Item2", result.get(0).getName());
+ assertEquals("test", result.get(0).getValue());
+ assertEquals("Item3", result.get(2).getName());
+ assertEquals(result.get(1),result.get(3));
+
+
+ result = JsonFunctions.getListFromPath("Item1///{1}Item3", rootList);
+
+ assertEquals(1, result.size());
+ assertEquals("item", result.get(0).getName());
+ assertEquals("val", result.get(0).getValue());
+
+ }
+
+
+ @Test
+ public void testGetListFromPathGoBack() {
+ ArrayList rootList = new ArrayList<>();
+ ListItem item1 = new ListItem();
+ item1.setName("Item1");
+ rootList.add(item1);
+ item1.setIsArray(true);
+
+ ArrayList> nestedListList = new ArrayList<>();
+
+ ArrayList nestedList = new ArrayList<>();
+ ListItem item2 = new ListItem();
+ item2.setName("Item2");
+
+ ArrayList innerListItem2 = new ArrayList<>();
+
+ ListItem item = new ListItem();
+ item.setName("item");
+ item.setValue("val");
+
+ innerListItem2.add(item);
+ item2.setObjects(innerListItem2);
+ nestedList.add(item2);
+
+ nestedListList.add(nestedList);
+
+ item1.setListObjects(nestedListList);
+
+ JsonData data = new JsonData();
+ data.setPath("Item1///{0}Item2");
+
+ ArrayList result = JsonFunctions.getListFromPath(data.getPath(), rootList);
+
+ assertEquals(1, result.size());
+ assertEquals("item", result.get(0).getName());
+ assertEquals("val", result.get(0).getValue());
+
+ data.goBack();
+
+ result = JsonFunctions.getListFromPath(data.getPath(), rootList);
+
+ assertEquals(2, result.size());
+ assertEquals("Item2", result.get(0).getName());
+ assertTrue(result.get(1).isSpace());
+
+ }
+
+
+}
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/IsArrayOfObjectsTest.java b/app/src/test/java/com/sjapps/jsonlist/java/IsArrayOfObjectsTest.java
new file mode 100644
index 0000000..ee450d9
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/IsArrayOfObjectsTest.java
@@ -0,0 +1,121 @@
+package com.sjapps.jsonlist.java;
+
+import static org.junit.Assert.*;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.sjapps.jsonlist.FileSystem;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class IsArrayOfObjectsTest {
+
+ @Test
+ public void testIsArrayOfObjects_true() {
+
+ JsonArray array = new JsonArray();
+ JsonObject object = new JsonObject();
+ object.addProperty("test","val");
+
+ array.add(object);
+
+ assertTrue(JsonFunctions.isArrayOfObjects(array));
+ }
+
+ @Test
+ public void testIsArrayOfObjects_true2() {
+
+ JsonArray array = new JsonArray();
+ JsonObject object = new JsonObject();
+ object.addProperty("test","val");
+ object.addProperty("test2",true);
+
+ JsonObject object2 = new JsonObject();
+ object.addProperty("test","val");
+ object.addProperty("test2",2);
+
+ array.add(object);
+ array.add(object2);
+
+ assertTrue(JsonFunctions.isArrayOfObjects(array));
+ }
+
+ @Test
+ public void testIsArrayOfObjects_true3() {
+
+ JsonArray array = new JsonArray();
+ JsonObject object = new JsonObject();
+ JsonObject object2 = new JsonObject();
+ object.addProperty("test","val");
+ object.addProperty("test2",2);
+ object.add("obj", object2);
+
+ array.add(object);
+
+ assertTrue(JsonFunctions.isArrayOfObjects(array));
+ }
+
+ @Test
+ public void testIsArrayOfObjects_true4() {
+
+ String data = "[{'test':'val'}]";
+ String data2 = "[{'test':'val'},{'test2':true}]";
+
+ JsonElement element = JsonParser.parseString(data);
+ JsonArray array = FileSystem.loadDataToJsonArray(element);
+
+ JsonElement element2 = JsonParser.parseString(data2);
+ JsonArray array2 = FileSystem.loadDataToJsonArray(element2);
+
+ assertTrue(JsonFunctions.isArrayOfObjects(array));
+ assertTrue(JsonFunctions.isArrayOfObjects(array2));
+ }
+
+ @Test
+ public void testIsArrayOfObjects_false() {
+
+ JsonArray array = new JsonArray();
+ array.add(1);
+ array.add("test");
+ array.add(true);
+
+ assertFalse(JsonFunctions.isArrayOfObjects(array));
+ }
+
+
+ @Test
+ public void testIsArrayOfObjects_false2() {
+
+ JsonArray array = new JsonArray();
+ array.add(1);
+ JsonObject object = new JsonObject();
+ object.addProperty("test",123);
+ array.add(object);
+ array.add("test");
+ array.add(true);
+
+ assertFalse(JsonFunctions.isArrayOfObjects(array));
+ }
+
+ @Test
+ public void testIsArrayOfObjects_false3() {
+
+ String data = "[{'test':'val'},123]";
+ String data2 = "[false,{'test':'val'},{'test2':true}]";
+
+ JsonElement element = JsonParser.parseString(data);
+ JsonArray array = FileSystem.loadDataToJsonArray(element);
+
+ JsonElement element2 = JsonParser.parseString(data2);
+ JsonArray array2 = FileSystem.loadDataToJsonArray(element2);
+
+ assertFalse(JsonFunctions.isArrayOfObjects(array));
+ assertFalse(JsonFunctions.isArrayOfObjects(array2));
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/sjapps/jsonlist/java/JsonFunctionsTest.java b/app/src/test/java/com/sjapps/jsonlist/java/JsonFunctionsTest.java
new file mode 100644
index 0000000..f8694c8
--- /dev/null
+++ b/app/src/test/java/com/sjapps/jsonlist/java/JsonFunctionsTest.java
@@ -0,0 +1,65 @@
+package com.sjapps.jsonlist.java;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class JsonFunctionsTest {
+
+ @Test
+ public void testGetJsonArrayRoot() {
+ // Create a sample JSON array
+ JsonArray jsonArray = new JsonArray();
+ jsonArray.add(new JsonObject());
+
+ ArrayList result = JsonFunctions.getJsonArrayRoot(jsonArray);
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("Objects Array", result.get(0).getName());
+ assertTrue(result.get(0).isArray());
+ assertEquals(1, result.get(0).getListObjects().size());
+ }
+
+ @Test
+ public void testGetJsonArray() {
+ // Create a sample JSON array with JSON objects
+ JsonArray jsonArray = new JsonArray();
+ jsonArray.add(new JsonObject());
+ jsonArray.add(new JsonObject());
+
+ ArrayList> result = JsonFunctions.getJsonArray(jsonArray);
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ }
+
+ @Test
+ public void testIsArrayOfObjects() {
+ // Create a sample JSON array with JSON objects
+ JsonArray jsonArray = new JsonArray();
+ jsonArray.add(new JsonObject());
+ jsonArray.add(new JsonObject());
+
+ boolean result = JsonFunctions.isArrayOfObjects(jsonArray);
+ assertTrue(result);
+ }
+
+ @Test
+ public void testGetJsonObject() {
+ // Create a sample JSON object
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("key1", "value1");
+ jsonObject.addProperty("key2", "value2");
+
+ ArrayList result = JsonFunctions.getJsonObject(jsonObject);
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ }
+
+}
diff --git a/images/jsonlist_main.jpg b/images/jsonlist_main.jpg
new file mode 100644
index 0000000..3fb4183
Binary files /dev/null and b/images/jsonlist_main.jpg differ
diff --git a/images/jsonlist_material_colots.gif b/images/jsonlist_material_colots.gif
new file mode 100644
index 0000000..a435c3b
Binary files /dev/null and b/images/jsonlist_material_colots.gif differ
diff --git a/images/jsonlist_splitview.jpg b/images/jsonlist_splitview.jpg
new file mode 100644
index 0000000..c1e15f1
Binary files /dev/null and b/images/jsonlist_splitview.jpg differ
diff --git a/images/jsonlist_splitview_landscape.jpg b/images/jsonlist_splitview_landscape.jpg
new file mode 100644
index 0000000..aa82397
Binary files /dev/null and b/images/jsonlist_splitview_landscape.jpg differ