diff --git a/Ganttproject_mobile/.idea/assetWizardSettings.xml b/Ganttproject_mobile/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..8da1b54 --- /dev/null +++ b/Ganttproject_mobile/.idea/assetWizardSettings.xml @@ -0,0 +1,47 @@ + + + + + + \ No newline at end of file diff --git a/Ganttproject_mobile/.idea/caches/build_file_checksums.ser b/Ganttproject_mobile/.idea/caches/build_file_checksums.ser index 0c9b48a..5f68904 100644 Binary files a/Ganttproject_mobile/.idea/caches/build_file_checksums.ser and b/Ganttproject_mobile/.idea/caches/build_file_checksums.ser differ diff --git a/Ganttproject_mobile/.idea/misc.xml b/Ganttproject_mobile/.idea/misc.xml index 3b488f3..19984ab 100644 --- a/Ganttproject_mobile/.idea/misc.xml +++ b/Ganttproject_mobile/.idea/misc.xml @@ -25,7 +25,7 @@ - + diff --git a/Ganttproject_mobile/.idea/vcs.xml b/Ganttproject_mobile/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/Ganttproject_mobile/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Ganttproject_mobile/app/build.gradle b/Ganttproject_mobile/app/build.gradle index 2fa9529..2ed9f94 100644 --- a/Ganttproject_mobile/app/build.gradle +++ b/Ganttproject_mobile/app/build.gradle @@ -3,9 +3,10 @@ apply plugin: 'com.google.protobuf' android { compileSdkVersion 27 + buildToolsVersion "27.0.3" defaultConfig { applicationId "com.bardsoftware.ganttproject_mobile" - minSdkVersion 15 + minSdkVersion 23 targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -19,6 +20,9 @@ android { } } +/*sourceSets { + main.java.srcDirs += "${protobuf.generatedFilesBaseDir}/main/javalite" +}*/ protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.0.0' @@ -34,7 +38,7 @@ protobuf { remove java } task.plugins { - javalite { } + javalite {} } } } @@ -44,9 +48,21 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.0' - testImplementation 'junit:junit:4.12' + implementation 'com.android.support:design:27.1.1' + androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation 'com.android.support:recyclerview-v7:27.1.1' + + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' implementation 'com.google.protobuf:protobuf-lite:3.0.0' + + implementation 'com.jakewharton:butterknife:8.8.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + + testImplementation 'junit:junit:4.12' + androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', { + exclude group: 'com.android.support', module: 'support-annotations' + }) } diff --git a/Ganttproject_mobile/app/src/main/AndroidManifest.xml b/Ganttproject_mobile/app/src/main/AndroidManifest.xml index bbcc2fd..a8ef1b7 100644 --- a/Ganttproject_mobile/app/src/main/AndroidManifest.xml +++ b/Ganttproject_mobile/app/src/main/AndroidManifest.xml @@ -2,6 +2,11 @@ + + + + + + \ No newline at end of file diff --git a/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/Constants.java b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/Constants.java new file mode 100644 index 0000000..aec4a0b --- /dev/null +++ b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/Constants.java @@ -0,0 +1,5 @@ +package com.bardsoftware.ganttproject_mobile; + +public class Constants { + public static final String FILE_PATH = "FilePath"; +} diff --git a/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/MainActivity.java b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/MainActivity.java index c48c5a0..7c48533 100644 --- a/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/MainActivity.java +++ b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/MainActivity.java @@ -7,7 +7,7 @@ public class MainActivity extends AppCompatActivity { - private static final int OPEN_FILE_CODE = 100 ; + private static final int OPEN_FILE_CODE = 100; @Override protected void onCreate(Bundle savedInstanceState) { @@ -16,14 +16,20 @@ protected void onCreate(Bundle savedInstanceState) { } public void openProtoFile(View view) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("*/*"); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("application/octet-stream"); startActivityForResult(intent, OPEN_FILE_CODE); } + protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == OPEN_FILE_CODE && resultCode == RESULT_OK && data != null) - { String filePath = data.getDataString(); - // do somthing... + if (requestCode == OPEN_FILE_CODE && resultCode == RESULT_OK && data != null) { + String filePath = data.getDataString(); + Bundle b = new Bundle(); + b.putString(Constants.FILE_PATH, filePath); + Class TasksActivity = TasksActivity.class; + Intent intent = new Intent(this, TasksActivity); + intent.putExtras(b); + startActivity(intent); } super.onActivityResult(requestCode, resultCode, data); diff --git a/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TaskAdapter.java b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TaskAdapter.java new file mode 100644 index 0000000..837ffda --- /dev/null +++ b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TaskAdapter.java @@ -0,0 +1,134 @@ +package com.bardsoftware.ganttproject_mobile; + +import android.content.Context; +import android.graphics.Paint; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.util.SparseBooleanArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageButton; +import android.widget.TextView; +import android.view.View.OnClickListener; + +import butterknife.BindView; +import butterknife.ButterKnife; + +import static dataformat.Dataformat.Task; +import static dataformat.Dataformat.Tasks; + +public class TaskAdapter extends RecyclerView.Adapter { + private Tasks.Builder tasksBuilder; + private SparseBooleanArray taskStateArray = new SparseBooleanArray(); + private int currentSelectedPosition = RecyclerView.NO_POSITION; + + @NonNull + @Override + public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + Context context = parent.getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + View view = inflater.inflate(R.layout.task_row, parent, false); + return new TaskAdapter.TaskViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull TaskViewHolder holder, final int position) { + if (tasksBuilder == null) return; + String taskName = tasksBuilder.getTask(position).getName(); + boolean itemIsChecked = tasksBuilder.getTask(position).getIsDone(); + taskStateArray.append(position, itemIsChecked); + holder.taskView.setText(taskName); + holder.taskCheckbox.setChecked(itemIsChecked); + if (itemIsChecked) { + holder.taskView.setPaintFlags(holder.taskView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } else + holder.taskView.setPaintFlags(holder.taskView.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + if (currentSelectedPosition == position) { + holder.deleteButton.setVisibility(View.VISIBLE); + holder.deleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + tasksBuilder.removeTask(position); + taskStateArray.delete(position); + notifyDataSetChanged(); + + } + }); + } else { + holder.deleteButton.setVisibility(View.GONE); + } + } + + @Override + public int getItemCount() { + if (tasksBuilder == null) return 0; + return tasksBuilder.getTaskCount(); + } + + public void setData(Tasks tasks1) { + tasksBuilder = tasks1.toBuilder(); + notifyDataSetChanged(); + } + + public Tasks getTasks() { + Tasks.Builder tasks1 = Tasks.newBuilder(); + for (int i = 0; i < getItemCount(); i++) { + Task task = Task.newBuilder() + .setName(tasksBuilder.getTask(i).getName()) + .setIsDone(tasksBuilder.getTask(i).getIsDone()) + .build(); + tasks1.addTask(task); + } + return tasks1.build(); + } + + public void addTask(String name) { + Task task = Task.newBuilder().setName(name).setIsDone(false).build(); + tasksBuilder.addTask(task); + taskStateArray.append(tasksBuilder.getTaskCount(), false); + notifyDataSetChanged(); + } + + public class TaskViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.task_textview) + TextView taskView; + @BindView(R.id.task_checkbox) + CheckBox taskCheckbox; + @BindView(R.id.delete_task_button) + ImageButton deleteButton; + + TaskViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + taskView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + currentSelectedPosition = getAdapterPosition(); + notifyDataSetChanged(); + } + }); + taskCheckbox.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + int adapterPosition = getAdapterPosition(); + if (!taskStateArray.get(adapterPosition, false)) { + taskCheckbox.setChecked(true); + taskView.setPaintFlags(taskView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + taskStateArray.put(adapterPosition, true); + Task task1 = Task.newBuilder().setName(tasksBuilder.getTask(adapterPosition).getName()).setIsDone(true).build(); + tasksBuilder.setTask(adapterPosition, task1); + } else { + taskCheckbox.setChecked(false); + taskStateArray.put(adapterPosition, false); + taskView.setPaintFlags(taskView.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + Task task1 = Task.newBuilder().setName(tasksBuilder.getTask(adapterPosition).getName()).setIsDone(false).build(); + tasksBuilder.setTask(adapterPosition, task1); + } + currentSelectedPosition = getAdapterPosition(); + notifyDataSetChanged(); + } + }); + } + } +} diff --git a/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TasksActivity.java b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TasksActivity.java new file mode 100644 index 0000000..70d49bc --- /dev/null +++ b/Ganttproject_mobile/app/src/main/java/com/bardsoftware/ganttproject_mobile/TasksActivity.java @@ -0,0 +1,130 @@ +package com.bardsoftware.ganttproject_mobile; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Parcelable; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Objects; + +import static dataformat.Dataformat.Tasks; + +public class TasksActivity extends AppCompatActivity { + private Uri filePath; + private Context mContext; + private TaskAdapter taskAdapter; + private Tasks tasks; + //@BindView(R.id.tasks_recyclerview) + RecyclerView tasksRecyclerView; + private static final String LAYOUT_POSITION = "position"; + private static final int REQUEST_EXTERNAL_STORAGE = 1; + private static String[] PERMISSIONS_STORAGE = { + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tasks); + mContext = this; + if (savedInstanceState == null) { + Bundle bundle = getIntent().getExtras(); + filePath = Uri.parse(Objects.requireNonNull(bundle).getString(Constants.FILE_PATH)); + } + verifyStoragePermissions(this); + + try { + InputStream input = mContext.getContentResolver().openInputStream(filePath); + tasks = Tasks.parseFrom(input); + + } catch (IOException e) { + e.printStackTrace(); + } + + tasksRecyclerView = findViewById(R.id.tasks_recyclerview); + tasksRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + taskAdapter = new TaskAdapter(); + tasksRecyclerView.setAdapter(taskAdapter); + taskAdapter.setData(tasks); + + if (savedInstanceState != null) { + Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable("position"); + tasksRecyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState); + } + } + + private static void verifyStoragePermissions(Activity activity) { + // Check if we have write permission + int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); + + if (permission != PackageManager.PERMISSION_GRANTED) { + // We don't have permission so prompt the user + ActivityCompat.requestPermissions( + activity, + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + } + } + + public void saveTasksFile(View view) { + taskAdapter.notifyDataSetChanged(); + tasks = taskAdapter.getTasks(); + mContext.grantUriPermission("com.bardsoftware.ganttproject_mobile", filePath, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + try { + + OutputStream output = mContext.getContentResolver().openOutputStream(filePath); + tasks.writeTo(Objects.requireNonNull(output)); + output.close(); + Toast.makeText(this, "file " + filePath.getPath() + " is saved", Toast.LENGTH_LONG).show(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void addTaskClicked(View view) { + taskAdapter.notifyDataSetChanged(); + + final EditText taskTitleEditText = new EditText(mContext); + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle("Add New Task"); + builder.setView(taskTitleEditText); + + builder.setPositiveButton("Add", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + String taskTitle = String.valueOf(taskTitleEditText.getText()); + taskAdapter.addTask(taskTitle); + } + + }); + builder.setNegativeButton("Cancel", null); + AlertDialog dialog = builder.create(); + dialog.show(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable(LAYOUT_POSITION, tasksRecyclerView.getLayoutManager().onSaveInstanceState()); + } +} diff --git a/Ganttproject_mobile/app/src/main/proto/dataformat.proto b/Ganttproject_mobile/app/src/main/proto/dataformat.proto new file mode 100644 index 0000000..4e2f2bd --- /dev/null +++ b/Ganttproject_mobile/app/src/main/proto/dataformat.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package dataformat; + +message Task { + string name = 1; + bool is_done = 2; +} +message Tasks { + repeated Task task= 1; +} \ No newline at end of file diff --git a/Ganttproject_mobile/app/src/main/res/drawable/ic_add_24dp.xml b/Ganttproject_mobile/app/src/main/res/drawable/ic_add_24dp.xml new file mode 100644 index 0000000..e500af0 --- /dev/null +++ b/Ganttproject_mobile/app/src/main/res/drawable/ic_add_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/Ganttproject_mobile/app/src/main/res/drawable/ic_delete_black_24dp.xml b/Ganttproject_mobile/app/src/main/res/drawable/ic_delete_black_24dp.xml new file mode 100644 index 0000000..4ebb6e8 --- /dev/null +++ b/Ganttproject_mobile/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/Ganttproject_mobile/app/src/main/res/layout/activity_tasks.xml b/Ganttproject_mobile/app/src/main/res/layout/activity_tasks.xml new file mode 100644 index 0000000..e779111 --- /dev/null +++ b/Ganttproject_mobile/app/src/main/res/layout/activity_tasks.xml @@ -0,0 +1,56 @@ + + + + + + + + + + +