Enhancement #779 : Add menu to share ZIM files (#866)

* Add issue#779.txt containing enhancement specifications

* UI Changes (Long click response - Contextual action bar)

* Fix Bug: List item selection bug, Add: Selected item highlighting

* Shift delete ZIM file functionality to CAB

* Add: Share ZIM file functionality to CAB (Cleanup to be done)

* Code clean up & comments

* Remove extra file

* Remove extra comment

* Update due to merge with master and pull request review

* Fix Bug: Share ZIM file on removable storage media (SD Card, USB Drive)
This commit is contained in:
Aditya-Sood 2018-11-12 02:25:15 +05:30 committed by Isaac Hutt
parent e053f5bf71
commit be1a6205ef
8 changed files with 182 additions and 8 deletions

View File

@ -186,7 +186,7 @@
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@ -17,21 +17,33 @@
* MA 02110-1301, USA.
*/
/**
* Fragment for list of downloaded ZIM files
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@ -41,6 +53,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.kiwix.kiwixmobile.BuildConfig;
import org.kiwix.kiwixmobile.KiwixApplication;
import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.base.BaseFragment;
@ -113,7 +126,122 @@ public class ZimFileSelectFragment extends BaseFragment
// Allow temporary use of ZimContentProvider to query books
ZimContentProvider.canIterate = true;
return llLayout; // We must return the loaded Layout
// Setting up Contextual Action Mode in response to selection of ZIM files in the ListView
mZimFileList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
mZimFileList.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
// Holds positions corresponding to every selected list item in the ListView
private ArrayList<Integer> selectedViewPosition = new ArrayList<Integer>();
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
if(checked) { // If the item was selected
selectedViewPosition.add(position);
mode.setTitle("" + selectedViewPosition.size()); // Update title of the CAB
}
else { // If the item was deselected
selectedViewPosition.remove(Integer.valueOf(position));
mode.setTitle("" + selectedViewPosition.size()); // Update title of the CAB
}
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate and setup the Contextual Action Bar (CAB)
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_zim_files_contextual, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // Leave the default implementation as is
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch(item.getItemId()) { // Determine the action item clicked on in the CAB, and respond accordingly
// Initiate file deletion functionality for each selected list item (file)
case R.id.zim_file_delete_item :
for(int i = 0; i < selectedViewPosition.size(); i++)
deleteSpecificZimDialog(selectedViewPosition.get(i)); // Individually confirm & initiate deletion for each selected file
mode.finish(); // Action performed, so close CAB
return true;
// Initiate file sharing functionality for each selected list item (file)
case R.id.zim_file_share_item :
// Create an implicit intent for sharing multiple selected files
Intent selectedFileShareIntent = new Intent();
selectedFileShareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
selectedFileShareIntent.setType("application/octet-stream"); // ZIM files are binary data without an Android-predefined subtype
ArrayList<Uri> selectedFileContentURIs = new ArrayList<Uri>(); // Store Content URIs for all selected files being shared
for(int i = 0; i < selectedViewPosition.size(); i++) {
LibraryNetworkEntity.Book data = (LibraryNetworkEntity.Book) mZimFileList.getItemAtPosition(selectedViewPosition.get(i));
String shareFilePath = data.file.getPath(); //Returns path to file in device storage
/**
* Using 'file:///' URIs directly is unsafe as it grants the intent receiving application
* the same file system permissions as the intent creating app.
*
* FileProvider instead offers 'content://' URIs for sharing files, which offer temporary
* access permissions to the file being shared (to the intent receiving application), which
* is fundamentally safer.
*/
File shareFile = new File(shareFilePath);
Uri shareContentUri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider", shareFile);
if(shareContentUri != null)
selectedFileContentURIs.add(shareContentUri); // Populate with the selected file content URIs
}
selectedFileShareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, selectedFileContentURIs); // Intent Extra for storing the array list of selected file content URIs
if(selectedFileContentURIs != null) { // Grant temporary access permission to the intent receiver for the content URIs
selectedFileShareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
/**
* Since a different app may be used for sharing everytime (E-mail, Cloud Upload, Wifi Sharing, etc.),
* so force an app chooser dialog every time some selected files are to be shared.
*/
Intent shareChooserIntent = Intent.createChooser(selectedFileShareIntent, getResources().getString(R.string.selected_file_cab_app_chooser_title));
if(shareChooserIntent.resolveActivity(getActivity().getPackageManager()) != null)
startActivity(shareChooserIntent); // Open the app chooser dialog
mode.finish(); // Action performed, so close CAB
return true;
default :
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Upon closure of the CAB, empty the array list of selected list item positions
selectedViewPosition.clear();
}
});
return llLayout; // Return the loaded Layout
}
@Override
@ -129,8 +257,8 @@ public class ZimFileSelectFragment extends BaseFragment
if (mZimFileList == null)
return;
// Long click response is the Contextual Action Bar (Selected file deletion & sharing)
mZimFileList.setOnItemClickListener(this);
mZimFileList.setOnItemLongClickListener(this);
Collections.sort(books, new FileComparator());
mFiles.clear();
mFiles.addAll(books);
@ -267,14 +395,14 @@ public class ZimFileSelectFragment extends BaseFragment
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
deleteSpecificZimDialog(position);
return true;
return false;
}
public void deleteSpecificZimDialog(int position) {
new AlertDialog.Builder(zimManageActivity, dialogStyle())
.setMessage(getString(R.string.delete_specific_zim))
.setMessage(mFiles.get(position).getTitle() + ": " + getString(R.string.delete_specific_zim))
.setPositiveButton(getResources().getString(R.string.delete), (dialog, which) -> {
// Toast messages updated for coherence with the new manner of file deletion (from CAB)
if (deleteSpecificZimFile(position)) {
Toast.makeText(zimManageActivity, getResources().getString(R.string.delete_specific_zim_toast), Toast.LENGTH_SHORT).show();
} else {

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
android:drawable="@color/greyed_out_selected"/>
</selector>

View File

@ -8,7 +8,8 @@
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@drawable/highlight">
<ImageView
android:id="@+id/favicon"

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/zim_file_delete_item"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/delete"
android:visible="true"
app:iconifiedByDefault="true"
app:showAsAction="always" />
<item
android:id="@+id/zim_file_share_item"
android:icon="@drawable/baseline_share_24"
android:title="@string/share"
android:visible="true"
app:iconifiedByDefault="true"
app:showAsAction="always"/>
</menu>
<!-- Menu layout for Contextual Action Bar in response to file selection in the library (ZimFileSelectFragment.java) -->

View File

@ -56,6 +56,8 @@
<string name="pref_clear_all_history_summary">Clear recent searches and tabs history</string>
<string name="all_history_cleared_toast">All History Cleared</string>
<string name="clear_all_history_dialog_title">Clear All History</string>
<string name="share">Share</string>
<string name="selected_file_cab_app_chooser_title">Share ZIM files with:</string>
<string name="delete">Delete</string>
<string name="delete_specific_search_toast">Recent search removed</string>
<string name="hint_contents_drawer_message">You can swipe left to view the contents of this article</string>

View File

@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
<external-path
name="external_files"
path="." />
<root-path
name="external_files"
path="/storage/" />
</paths>