Merge branch 'develop' into iadeelzafar/wifi-hotspot

This commit is contained in:
Adeel 2019-08-23 15:02:45 +05:00
commit 316b9d9550
7 changed files with 83 additions and 111 deletions

View File

@ -58,6 +58,8 @@ If you wish to rebase you should be following the [Golden Rule](https://www.atla
The default build is `debug`, with this variant you can use a debugger while developing. To install the application click the `run` button in Android Studio with the `app` configuration selected while you have a device connected. All other build types but `release` can be ignored, the `release` build is what gets uploaded to the Google Play store and can be built locally with the dummy credentials/keystore provided. The default build is `debug`, with this variant you can use a debugger while developing. To install the application click the `run` button in Android Studio with the `app` configuration selected while you have a device connected. All other build types but `release` can be ignored, the `release` build is what gets uploaded to the Google Play store and can be built locally with the dummy credentials/keystore provided.
By default we fetch kiwix-lib, the key component for interacting with ZIM files from maven, but if you wish to use your own locally compiled version for testing purposes, then you can create the directory `app/libs` and place your .aar file inside it to have it used instead.
### Testing ### Testing
Unit tests are located in `app/src/test` and to run them locally you Unit tests are located in `app/src/test` and to run them locally you

View File

@ -50,12 +50,13 @@ String[] archs = ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']
dependencies { dependencies {
// use jdk8 java.time backport, as long app < Build.VERSION_CODES.O // use jdk8 java.time backport, as long app < Build.VERSION_CODES.O
implementation("com.jakewharton.threetenabp:threetenabp:1.1.1") implementation("com.jakewharton.threetenabp:threetenabp:1.1.1")
// Get kiwixlib online if it is not populated locally // Get kiwixlib online if it is not populated locally
if (file("../kiwixlib/src/main").list().length == 1) { if (!shouldUseLocalVersion()) {
implementation 'org.kiwix.kiwixlib:kiwixlib:1.0.12' implementation 'org.kiwix.kiwixlib:kiwixlib:6.0.2'
} else { } else {
implementation project(":kiwixlib") implementation 'com.getkeepsafe.relinker:relinker:1.3.1'
archs = file("../kiwixlib/src/main/jniLibs").list() implementation fileTree(include: ['*.aar'], dir: 'libs')
} }
// Android Support // Android Support
@ -147,6 +148,10 @@ dependencies {
} }
} }
private boolean shouldUseLocalVersion() {
file("./libs").exists()
}
// Set custom app import directory // Set custom app import directory
def map = [:] def map = [:]
def custom = new File("app/src") def custom = new File("app/src")

View File

@ -125,7 +125,7 @@ public class ZimContentProvider extends ContentProvider {
* *
* Note that the value returned is NOT unique for each zim file. Versions of the same wiki * Note that the value returned is NOT unique for each zim file. Versions of the same wiki
* (complete, nopic, novid, etc) may return the same title. * (complete, nopic, novid, etc) may return the same title.
* */ */
public static String getZimFileTitle() { public static String getZimFileTitle() {
if (currentJNIReader == null || zimFileName == null) { if (currentJNIReader == null || zimFileName == null) {
return null; return null;
@ -409,10 +409,8 @@ public class ZimContentProvider extends ContentProvider {
String fileName = uri.toString(); String fileName = uri.toString();
fileName = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.length()); fileName = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.length());
File f = new File(FileUtils.getFileCacheDir(getContext()), fileName); File f = new File(FileUtils.getFileCacheDir(getContext()), fileName);
JNIKiwixString mime = new JNIKiwixString(); byte[] data = currentJNIReader.getContent(new JNIKiwixString(filePath), new JNIKiwixString(),
JNIKiwixString title = new JNIKiwixString(); new JNIKiwixString(), new JNIKiwixInt());
JNIKiwixInt size = new JNIKiwixInt();
byte[] data = currentJNIReader.getContent(filePath, title, mime, size);
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(data, 0, data.length); out.write(data, 0, data.length);
out.flush(); out.flush();
@ -473,10 +471,10 @@ public class ZimContentProvider extends ContentProvider {
@Override @Override
public void run() { public void run() {
try { try {
JNIKiwixString mime = new JNIKiwixString(); final JNIKiwixString mime = new JNIKiwixString();
JNIKiwixString title = new JNIKiwixString(); final JNIKiwixInt size = new JNIKiwixInt();
JNIKiwixInt size = new JNIKiwixInt(); final JNIKiwixString url = new JNIKiwixString(this.articleZimUrl);
byte[] data = currentJNIReader.getContent(articleZimUrl, title, mime, size); byte[] data = currentJNIReader.getContent(url, new JNIKiwixString(), mime, size);
if (mime.value != null && mime.value.equals("text/css") && MainActivity.nightMode) { if (mime.value != null && mime.value.equals("text/css") && MainActivity.nightMode) {
out.write(("img, video { \n" + out.write(("img, video { \n" +
" -webkit-filter: invert(1); \n" + " -webkit-filter: invert(1); \n" +
@ -486,7 +484,7 @@ public class ZimContentProvider extends ContentProvider {
out.write(data, 0, data.length); out.write(data, 0, data.length);
out.flush(); out.flush();
Log.d(TAG_KIWIX, "reading " + articleZimUrl Log.d(TAG_KIWIX, "reading " + url.value
+ "(mime: " + mime.value + ", size: " + size.value + ") finished."); + "(mime: " + mime.value + ", size: " + size.value + ") finished.");
} catch (IOException | NullPointerException e) { } catch (IOException | NullPointerException e) {
Log.e(TAG_KIWIX, "Exception reading article " + articleZimUrl + " from zim file", Log.e(TAG_KIWIX, "Exception reading article " + articleZimUrl + " from zim file",

View File

@ -20,7 +20,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
@ -28,20 +27,17 @@ import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import butterknife.BindView;
import org.kiwix.kiwixmobile.BuildConfig; import butterknife.ButterKnife;
import org.kiwix.kiwixmobile.R; import butterknife.Unbinder;
import org.kiwix.kiwixmobile.data.ZimContentProvider;
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import org.kiwix.kiwixmobile.BuildConfig;
import butterknife.BindView; import org.kiwix.kiwixmobile.R;
import butterknife.ButterKnife; import org.kiwix.kiwixmobile.data.ZimContentProvider;
import butterknife.Unbinder; import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
import static org.kiwix.kiwixmobile.utils.Constants.NOTES_DIRECTORY; import static org.kiwix.kiwixmobile.utils.Constants.NOTES_DIRECTORY;
@ -55,7 +51,7 @@ import static org.kiwix.kiwixmobile.utils.Constants.NOTES_DIRECTORY;
*/ */
public class AddNoteDialog extends DialogFragment public class AddNoteDialog extends DialogFragment
implements ConfirmationAlertDialogFragment.UserClickListener { implements ConfirmationAlertDialogFragment.UserClickListener {
public static final String TAG = "AddNoteDialog"; public static final String TAG = "AddNoteDialog";
@ -73,14 +69,12 @@ public class AddNoteDialog extends DialogFragment
private String zimFileName; private String zimFileName;
private String zimFileTitle; private String zimFileTitle;
private String articleTitle; private String articleTitle;
// Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
private String zimNoteDirectoryName;
// Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt" // Corresponds to "ArticleUrl" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
private String articleNotefileName; private String articleNotefileName;
private boolean noteFileExists = false; private boolean noteFileExists = false;
boolean noteEdited = false; // Keeps track of state of the note (whether edited since last save) private boolean noteEdited = false; // Keeps track of state of the note (whether edited since last save)
private String ZIM_NOTES_DIRECTORY; // Stores path to directory for the currently open zim's notes private String zimNotesDirectory; // Stores path to directory for the currently open zim's notes
public AddNoteDialog(@NonNull SharedPreferenceUtil sharedPreferenceUtil) { public AddNoteDialog(@NonNull SharedPreferenceUtil sharedPreferenceUtil) {
this.sharedPreferenceUtil = sharedPreferenceUtil; this.sharedPreferenceUtil = sharedPreferenceUtil;
@ -91,8 +85,8 @@ public class AddNoteDialog extends DialogFragment
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, setStyle(DialogFragment.STYLE_NORMAL,
sharedPreferenceUtil.nightMode() ? R.style.AddNoteDialogStyle_Night sharedPreferenceUtil.nightMode() ? R.style.AddNoteDialogStyle_Night
: R.style.AddNoteDialogStyle); : R.style.AddNoteDialogStyle);
// Returns name of the form ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim" // Returns name of the form ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim"
zimFileName = ZimContentProvider.getZimFile(); zimFileName = ZimContentProvider.getZimFile();
@ -101,49 +95,47 @@ public class AddNoteDialog extends DialogFragment
zimFileTitle = ZimContentProvider.getZimFileTitle(); zimFileTitle = ZimContentProvider.getZimFileTitle();
articleTitle = ((MainActivity) getActivity()).getCurrentWebView().getTitle(); articleTitle = ((MainActivity) getActivity()).getCurrentWebView().getTitle();
zimNoteDirectoryName = getZimNoteDirectoryName(); // Corresponds to "ZimFileName" of "{External Storage}/Kiwix/Notes/ZimFileName/ArticleUrl.txt"
String zimNoteDirectoryName = getZimNoteDirectoryName();
articleNotefileName = getArticleNotefileName(); articleNotefileName = getArticleNotefileName();
ZIM_NOTES_DIRECTORY = NOTES_DIRECTORY + zimNoteDirectoryName + "/"; zimNotesDirectory = NOTES_DIRECTORY + zimNoteDirectoryName + "/";
} else { } else {
showToast(R.string.error_filenotfound, Toast.LENGTH_LONG); onFailureToCreateAddNoteDialog();
closeKeyboard();
getFragmentManager().beginTransaction().remove(AddNoteDialog.this).commit();
} }
} }
private void onFailureToCreateAddNoteDialog() {
showToast(R.string.error_filenotfound, Toast.LENGTH_LONG);
closeKeyboard();
getFragmentManager().beginTransaction().remove(this).commit();
}
@Override @Override
public @NonNull View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public @NonNull View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.dialog_add_note, container, false); View view = inflater.inflate(R.layout.dialog_add_note, container, false);
unbinder = ButterKnife.bind(this, view); unbinder = ButterKnife.bind(this, view);
toolbar.setTitle(R.string.note); toolbar.setTitle(R.string.note);
toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp); toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() { toolbar.setNavigationOnClickListener(v -> {
@Override closeKeyboard();
public void onClick(View v) { exitAddNoteDialog();
closeKeyboard();
exitAddNoteDialog();
}
}); });
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { toolbar.setOnMenuItemClickListener(item -> {
@Override switch (item.getItemId()) {
public boolean onMenuItemClick(MenuItem item) { case R.id.share_note: // Opens app-chooser for sharing the note text file
shareNote();
break;
switch (item.getItemId()) { case R.id.save_note: // Saves the note as a text file
case R.id.share_note: // Opens app-chooser for sharing the note text file saveNote(addNoteEditText.getText().toString());
shareNote(); break;
break;
case R.id.save_note: // Saves the note as a text file
saveNote(addNoteEditText.getText().toString());
break;
}
return true;
} }
return true;
}); });
toolbar.inflateMenu(R.menu.menu_add_note_dialog); toolbar.inflateMenu(R.menu.menu_add_note_dialog);
@ -185,7 +177,12 @@ public class AddNoteDialog extends DialogFragment
// Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html" // Returns url of the form: "content://org.kiwix.kiwixmobile.zim.base/A/Main_Page.html"
String articleUrl = ((MainActivity) getActivity()).getCurrentWebView().getUrl(); String articleUrl = ((MainActivity) getActivity()).getCurrentWebView().getUrl();
String notefileName = getTextAfterLastSlashWithoutExtension(articleUrl); String notefileName = "";
if (articleUrl == null) {
onFailureToCreateAddNoteDialog();
} else {
notefileName = getTextAfterLastSlashWithoutExtension(articleUrl);
}
return (!notefileName.isEmpty()) ? notefileName : articleTitle; return (!notefileName.isEmpty()) ? notefileName : articleTitle;
} }
@ -202,10 +199,11 @@ public class AddNoteDialog extends DialogFragment
int rightmostDot = path.lastIndexOf('.'); int rightmostDot = path.lastIndexOf('.');
if (rightmostSlash > -1 && rightmostDot > -1) { if (rightmostSlash > -1 && rightmostDot > -1) {
return (path.substring(rightmostSlash + 1, rightmostDot)); return path.substring(
rightmostSlash + 1, (rightmostDot > rightmostSlash) ? rightmostDot : path.length());
} }
return ""; // If couldn't find the dot and/or slash return ""; // If couldn't find a dot and/or slash in the url
} }
// Override onBackPressed() to respond to user pressing 'Back' button on navigation bar // Override onBackPressed() to respond to user pressing 'Back' button on navigation bar
@ -220,17 +218,17 @@ public class AddNoteDialog extends DialogFragment
}; };
} }
void exitAddNoteDialog() { private void exitAddNoteDialog() {
if (noteEdited) { if (noteEdited) {
Fragment previousInstance = getActivity().getSupportFragmentManager() Fragment previousInstance = getActivity().getSupportFragmentManager()
.findFragmentByTag(ConfirmationAlertDialogFragment.TAG); .findFragmentByTag(ConfirmationAlertDialogFragment.TAG);
if (previousInstance == null) { if (previousInstance == null) {
// Custom AlertDialog for taking user confirmation before closing note dialog in case of unsaved changes // Custom AlertDialog for taking user confirmation before closing note dialog in case of unsaved changes
DialogFragment newFragment = new ConfirmationAlertDialogFragment(sharedPreferenceUtil, TAG, DialogFragment newFragment = new ConfirmationAlertDialogFragment(sharedPreferenceUtil, TAG,
R.string.confirmation_alert_dialog_message); R.string.confirmation_alert_dialog_message);
newFragment.show(getActivity().getSupportFragmentManager(), newFragment.show(getActivity().getSupportFragmentManager(),
ConfirmationAlertDialogFragment.TAG); ConfirmationAlertDialogFragment.TAG);
} }
} else { } else {
// Closing unedited note dialog straightaway // Closing unedited note dialog straightaway
@ -251,7 +249,7 @@ public class AddNoteDialog extends DialogFragment
} }
} }
void enableSaveNoteMenuItem() { private void enableSaveNoteMenuItem() {
if (toolbar.getMenu() != null) { if (toolbar.getMenu() != null) {
MenuItem saveItem = toolbar.getMenu().findItem(R.id.save_note); MenuItem saveItem = toolbar.getMenu().findItem(R.id.save_note);
saveItem.setEnabled(true); saveItem.setEnabled(true);
@ -261,7 +259,7 @@ public class AddNoteDialog extends DialogFragment
} }
} }
void enableShareNoteMenuItem() { private void enableShareNoteMenuItem() {
if (toolbar.getMenu() != null) { if (toolbar.getMenu() != null) {
MenuItem shareItem = toolbar.getMenu().findItem(R.id.share_note); MenuItem shareItem = toolbar.getMenu().findItem(R.id.share_note);
shareItem.setEnabled(true); shareItem.setEnabled(true);
@ -284,17 +282,17 @@ public class AddNoteDialog extends DialogFragment
private void showKeyboard() { private void showKeyboard() {
InputMethodManager inputMethodManager = InputMethodManager inputMethodManager =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
} }
void closeKeyboard() { private void closeKeyboard() {
InputMethodManager inputMethodManager = InputMethodManager inputMethodManager =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
} }
void saveNote(String noteText) { private void saveNote(String noteText) {
/* String content of the EditText, given by noteText, is saved into the text file given by: /* String content of the EditText, given by noteText, is saved into the text file given by:
* "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt" * "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt"
@ -303,13 +301,13 @@ public class AddNoteDialog extends DialogFragment
if (isExternalStorageWritable()) { if (isExternalStorageWritable()) {
if (ContextCompat.checkSelfPermission(getContext(), if (ContextCompat.checkSelfPermission(getContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "WRITE_EXTERNAL_STORAGE permission not granted"); Log.d(TAG, "WRITE_EXTERNAL_STORAGE permission not granted");
showToast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG); showToast(R.string.note_save_unsuccessful, Toast.LENGTH_LONG);
return; return;
} }
File notesFolder = new File(ZIM_NOTES_DIRECTORY); File notesFolder = new File(zimNotesDirectory);
boolean folderExists = true; boolean folderExists = true;
if (!notesFolder.exists()) { if (!notesFolder.exists()) {
@ -347,14 +345,14 @@ public class AddNoteDialog extends DialogFragment
* is displayed in the EditText field (note content area) * is displayed in the EditText field (note content area)
* */ * */
File noteFile = new File(ZIM_NOTES_DIRECTORY + articleNotefileName + ".txt"); File noteFile = new File(zimNotesDirectory + articleNotefileName + ".txt");
if (noteFile.exists()) { if (noteFile.exists()) {
noteFileExists = true; noteFileExists = true;
StringBuilder contents = new StringBuilder(); StringBuilder contents = new StringBuilder();
try (BufferedReader input = new BufferedReader(new java.io.FileReader(noteFile))) { try (BufferedReader input = new BufferedReader(new java.io.FileReader(noteFile))) {
String line = null; String line;
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
contents.append(line); contents.append(line);
@ -373,7 +371,7 @@ public class AddNoteDialog extends DialogFragment
// No action in case the note file for the currently open article doesn't exist // No action in case the note file for the currently open article doesn't exist
} }
void shareNote() { private void shareNote() {
/* The note text file corresponding to the currently open article, given at: /* The note text file corresponding to the currently open article, given at:
* "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt" * "{External Storage}/Kiwix/Notes/ZimFileTitle/ArticleTitle.txt"
@ -382,10 +380,10 @@ public class AddNoteDialog extends DialogFragment
if (noteEdited) { if (noteEdited) {
saveNote( saveNote(
addNoteEditText.getText().toString()); // Save edited note before sharing the text file addNoteEditText.getText().toString()); // Save edited note before sharing the text file
} }
File noteFile = new File(ZIM_NOTES_DIRECTORY + articleNotefileName + ".txt"); File noteFile = new File(zimNotesDirectory + articleNotefileName + ".txt");
Uri noteFileUri = null; Uri noteFileUri = null;
if (noteFile.exists()) { if (noteFile.exists()) {
@ -394,8 +392,8 @@ public class AddNoteDialog extends DialogFragment
// From Nougat 7 (API 24) access to files is shared temporarily with other apps // From Nougat 7 (API 24) access to files is shared temporarily with other apps
// Need to use FileProvider for the same // Need to use FileProvider for the same
noteFileUri = noteFileUri =
FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider", FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider",
noteFile); noteFile);
} else { } else {
noteFileUri = Uri.fromFile(noteFile); noteFileUri = Uri.fromFile(noteFile);
} }
@ -409,7 +407,7 @@ public class AddNoteDialog extends DialogFragment
noteFileShareIntent.putExtra(Intent.EXTRA_STREAM, noteFileUri); noteFileShareIntent.putExtra(Intent.EXTRA_STREAM, noteFileUri);
noteFileShareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); noteFileShareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent shareChooser = Intent.createChooser(noteFileShareIntent, Intent shareChooser = Intent.createChooser(noteFileShareIntent,
getString(R.string.note_share_app_chooser_title)); getString(R.string.note_share_app_chooser_title));
if (noteFileShareIntent.resolveActivity(getActivity().getPackageManager()) != null) { if (noteFileShareIntent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(shareChooser); startActivity(shareChooser);

View File

@ -1,24 +0,0 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion")
testImplementation "junit:junit:$jUnitVersion"
}

View File

View File

@ -1,7 +0,0 @@
<manifest package="kiwix.org.kiwixlib">
<application>
</application>
</manifest>