mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Merge pull request #850 from kiwix/abdulwd/merge
Merge master branch with 3.0
This commit is contained in:
commit
40e74aec1f
45
.github/ISSUE_TEMPLATE.md
vendored
45
.github/ISSUE_TEMPLATE.md
vendored
@ -1,45 +0,0 @@
|
||||
# Fill out this template with your issue then delete these lines only leaving the titles and your responses.
|
||||
|
||||
# For a Bug Report:
|
||||
|
||||
## Bug Report
|
||||
|
||||
### Are you ready to report?
|
||||
- Your issue may already be reported! Please search on the [issue track](../) before creating one.
|
||||
- Ensure you have tested with the last (dev) version of the software. (If not, and if possible, run your test again with the most recent version available.)
|
||||
|
||||
### Environment
|
||||
* version of the software you use : _____
|
||||
* device / operating system : _____
|
||||
|
||||
### The Bug:
|
||||
|
||||
* What do you obtain? A copy of the error message or a screenshot is always useful.
|
||||
|
||||
* When does this occur?
|
||||
|
||||
#### Steps to reproduce:
|
||||
|
||||
1. _____
|
||||
2. _____
|
||||
3. _____
|
||||
...
|
||||
|
||||
* What should be the behaviour from your point of view? How do you expect the service to work?
|
||||
|
||||
|
||||
### Other Comments:
|
||||
* What is the context of this ticket? If not obvious, explain why you need to do this.
|
||||
|
||||
* If you have an idea about the technical background of the ticket, please share it.
|
||||
|
||||
|
||||
# For a Feature Request:
|
||||
|
||||
## Feature Request
|
||||
|
||||
#### Feature Description
|
||||
|
||||
Please describe the feature you want to add to the project.
|
||||
|
||||
|
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!-- Checklist for reporting a bug
|
||||
- Check the issue tracker. The issue you have may have already been reported.
|
||||
- Ensure you have tested the latest developers version.
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Steps to reproduce the behavior:**
|
||||
1.
|
||||
<!--
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
**Screenshots**
|
||||
<!-- If applicable, add screenshots to help explain your problem otherwise delete this heading. -->
|
||||
|
||||
**Environment**
|
||||
- Version of Kiwix Android :
|
||||
- Device :
|
||||
- OS version :
|
||||
|
||||
**Logs**
|
||||
<!-- If applicable, add logs to help the developers in identifying your problem otherwise delete this heading. -->
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!-- Checklist for requesting a feature:
|
||||
- Check the issue tracker. The feature you are requesting may have already been requested.
|
||||
- Check if the feature does not exists on the latest developers version.
|
||||
-->
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Example: I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered.
|
||||
If not applicable delete this heading. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the feature request here.
|
||||
If not applicable delete this heading. -->
|
31
.github/ISSUE_TEMPLATE/test_request.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/test_request.md
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Request Tests
|
||||
about: Identify code liable to break and create tests to prevent software regression due to code changes
|
||||
|
||||
---
|
||||
|
||||
<!-- Checklist for reporting a bug
|
||||
- Check the issue tracker. The issue you have may have already been reported.
|
||||
- Ensure that you are using the latest developers version.
|
||||
- Make sure that the tests are not for any other framework libraries that are being used in the code.
|
||||
- Prefer code that seems to get changed by different developers and accessed by a lot of other modules.
|
||||
-->
|
||||
|
||||
**Type of Test**
|
||||
<!-- Unit Test or UI Test -->
|
||||
* [ ] Unit Test
|
||||
* [ ] UI Test
|
||||
|
||||
**Particular area and files to be tested**
|
||||
<!--
|
||||
In case of Unit Tests, mention the names of the files or modules to be tested.
|
||||
In case of UI Tests, mention the names of the activities to be tested.
|
||||
-->
|
||||
|
||||
**Current Status of the tests**
|
||||
<!-- Are there any tests included for this particular area, which need improvement, or has this are never been tested at all?
|
||||
(You can see the tests that we are currently using in the "androidTest", and "androidTestKiwix" folders in the "app/src" folder) -->
|
||||
|
||||
**Particular Behaviour to be tested**
|
||||
<!-- Give a brief description of the particular cases which need to be tested.
|
||||
(For example: test whether data is saved in the database on clicking a particular button, or test whether the default language is preselected, etc) -->
|
@ -1,4 +1,4 @@
|
||||
2.3
|
||||
2.4
|
||||
FIX: External SD card problems
|
||||
FIX: Some UI translation
|
||||
FIX: Download manager process sporadic problems
|
||||
|
@ -76,7 +76,7 @@ public class BasicTest {
|
||||
openDrawerWithGravity(R.id.drawer_layout, Gravity.RIGHT);
|
||||
assertDrawerIsOpenWithGravity(R.id.drawer_layout, Gravity.RIGHT);
|
||||
|
||||
assertDisplayed(R.string.no_section_info);
|
||||
assertDisplayed(R.string.menu_help);
|
||||
|
||||
closeDrawerWithGravity(R.id.drawer_layout, Gravity.RIGHT);
|
||||
assertDrawerIsClosedWithGravity(R.id.drawer_layout, Gravity.RIGHT);*/
|
||||
|
@ -2,6 +2,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Home</title>
|
||||
<title>REPLACE_menu_home</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="display:none;">REPLACE_menu_home</h1>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -23,6 +23,7 @@ import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
import org.kiwix.kiwixmobile.utils.LanguageUtils;
|
||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -42,6 +43,7 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
AndroidInjection.inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
LanguageUtils.handleLocaleChange(this, sharedPreferenceUtil);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,6 +36,7 @@ import org.kiwix.kiwixmobile.data.local.entity.LibraryDatabaseEntity;
|
||||
import org.kiwix.kiwixmobile.data.local.entity.NetworkLanguageDatabaseEntity;
|
||||
import org.kiwix.kiwixmobile.data.local.entity.RecentSearch;
|
||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity;
|
||||
import org.kiwix.kiwixmobile.utils.UpdateUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
@ -52,7 +53,7 @@ import static org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX;
|
||||
@Singleton
|
||||
public class KiwixDatabase extends SquidDatabase {
|
||||
|
||||
private static final int VERSION = 16;
|
||||
private static final int VERSION = 17;
|
||||
private final Context context;
|
||||
|
||||
@Inject
|
||||
@ -125,6 +126,8 @@ public class KiwixDatabase extends SquidDatabase {
|
||||
tryAddColumn(Bookmark.ZIM_FILE_PATH);
|
||||
tryAddColumn(Bookmark.FAVICON);
|
||||
migrateBookmarksVersion16();
|
||||
case 16:
|
||||
new BookmarksDao(this).processBookmark(UpdateUtils::reformatProviderUrl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.data.local.dao;
|
||||
|
||||
import com.yahoo.squidb.data.SquidCursor;
|
||||
import com.yahoo.squidb.sql.Query;
|
||||
import com.yahoo.squidb.sql.Update;
|
||||
|
||||
import org.kiwix.kiwixmobile.data.ZimContentProvider;
|
||||
import org.kiwix.kiwixmobile.data.local.KiwixDatabase;
|
||||
@ -107,4 +108,23 @@ public class BookmarksDao {
|
||||
kiwixDatabase.deleteWhere(Bookmark.class, Bookmark.BOOKMARK_URL.eq(bookmark.getBookmarkUrl())
|
||||
.and(Bookmark.ZIM_ID.eq(bookmark.getZimId())));
|
||||
}
|
||||
|
||||
public void processBookmark(StringOperation operation) {
|
||||
try (SquidCursor<Bookmark> bookmarkCursor = kiwixDatabase.query(Bookmark.class,
|
||||
Query.select(Bookmark.ID, Bookmark.BOOKMARK_URL))) {
|
||||
while (bookmarkCursor.moveToNext()) {
|
||||
String url = bookmarkCursor.get(Bookmark.BOOKMARK_URL);
|
||||
url = operation.apply(url);
|
||||
if (url != null) {
|
||||
kiwixDatabase.update(Update.table(Bookmark.TABLE)
|
||||
.where(Bookmark.ID.eq(bookmarkCursor.get(Bookmark.ID)))
|
||||
.set(Bookmark.BOOKMARK_URL, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface StringOperation {
|
||||
String apply(String string);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public class ChunkUtils {
|
||||
|
||||
public static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
|
||||
public static final String ZIM_EXTENSION = ".zim";
|
||||
public static final String PART = ".part";
|
||||
public static final String PART = ".part.part";
|
||||
public static final long CHUNK_SIZE = 1024L * 1024L * 1024L * 2L;
|
||||
|
||||
public static List<Chunk> getChunks(String url, long contentLength, int notificationID) {
|
||||
@ -63,7 +63,7 @@ public class ChunkUtils {
|
||||
|
||||
private static String[] getZimChunkFileNames(String fileName, int count) {
|
||||
if (count == 1) {
|
||||
return new String[] { fileName + PART};
|
||||
return new String[]{fileName + PART};
|
||||
}
|
||||
int position = fileName.lastIndexOf(".");
|
||||
String baseName = position > 0 ? fileName.substring(0, position) : fileName;
|
||||
|
@ -71,6 +71,9 @@ import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSource;
|
||||
|
||||
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.ALPHABET;
|
||||
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.PART;
|
||||
import static org.kiwix.kiwixmobile.downloader.ChunkUtils.ZIM_EXTENSION;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_BOOK;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_LIBRARY;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_NOTIFICATION_ID;
|
||||
@ -139,8 +142,7 @@ public class DownloadService extends Service {
|
||||
if (intent == null) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
String log = intent.getAction();
|
||||
log += " : ";
|
||||
String log = intent.getAction() + " : ";
|
||||
if (intent.hasExtra(NOTIFICATION_ID)) {
|
||||
log += intent.getIntExtra(NOTIFICATION_ID, -3);
|
||||
}
|
||||
@ -322,6 +324,35 @@ public class DownloadService extends Service {
|
||||
notification.get(notificationID).setContentText(getString(R.string.zim_file_downloaded));
|
||||
final Intent target = new Intent(DownloadService.this, MainActivity.class);
|
||||
target.putExtra(EXTRA_ZIM_FILE, KIWIX_ROOT + StorageUtils.getFileNameFromUrl(book.getUrl()));
|
||||
//Remove the extra ".part" from files
|
||||
String filename = book.file.getPath();
|
||||
if (filename.endsWith(ZIM_EXTENSION)) {
|
||||
filename = filename + PART;
|
||||
File partFile = new File(filename);
|
||||
if (partFile.exists()) {
|
||||
partFile.renameTo(new File(partFile.getPath().replaceAll(".part", "")));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; true; i++) {
|
||||
char first = ALPHABET.charAt(i / 26);
|
||||
char second = ALPHABET.charAt(i % 26);
|
||||
String chunkExtension = String.valueOf(first) + second;
|
||||
filename = book.file.getPath();
|
||||
filename = filename.replaceAll(".zim([a-z][a-z]){0,1}$", ".zim");
|
||||
filename = filename + chunkExtension + ".part";
|
||||
File partFile = new File(filename);
|
||||
if (partFile.exists()) {
|
||||
partFile.renameTo(new File(partFile.getPath().replaceAll(".part$", "")));
|
||||
} else {
|
||||
File lastChunkFile = new File(filename + ".part");
|
||||
if (lastChunkFile.exists()) {
|
||||
lastChunkFile.renameTo(new File(partFile.getPath().replaceAll(".part", "")));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
target.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity
|
||||
(getBaseContext(), 0,
|
||||
@ -453,7 +484,7 @@ public class DownloadService extends Service {
|
||||
// Create chunk file
|
||||
File file = new File(KIWIX_ROOT, chunk.getFileName());
|
||||
file.getParentFile().mkdirs();
|
||||
File fullFile = new File(file.getPath().substring(0, file.getPath().length() - 5));
|
||||
File fullFile = new File(file.getPath().substring(0, file.getPath().length() - PART.length()));
|
||||
|
||||
long downloaded = Long.parseLong(chunk.getRangeHeader().split("-")[0]);
|
||||
if (fullFile.exists() && fullFile.length() == chunk.getSize()) {
|
||||
@ -535,7 +566,8 @@ public class DownloadService extends Service {
|
||||
break;
|
||||
}
|
||||
|
||||
if (MainActivity.wifiOnly && !NetworkUtils.isWiFi(getApplicationContext())) {
|
||||
if (MainActivity.wifiOnly && !NetworkUtils.isWiFi(getApplicationContext()) ||
|
||||
!NetworkUtils.isNetworkAvailable(getApplicationContext())) {
|
||||
pauseDownload(chunk.getNotificationID());
|
||||
}
|
||||
|
||||
@ -594,16 +626,16 @@ public class DownloadService extends Service {
|
||||
if (downloadStatus.get(chunk.getNotificationID()) == CANCEL) {
|
||||
String path = file.getPath();
|
||||
Log.i(KIWIX_TAG, "Download Cancelled, deleting file: " + path);
|
||||
if (path.substring(path.length() - 8).equals("zim.part")) {
|
||||
path = path.substring(0, path.length() - 5);
|
||||
if (path.substring(path.length() - (ZIM_EXTENSION + PART).length()).equals(ZIM_EXTENSION + PART)) {
|
||||
path = path.substring(0, path.length() - PART.length() + 1);
|
||||
FileUtils.deleteZimFile(path);
|
||||
} else {
|
||||
path = path.substring(0, path.length() - 7) + "aa";
|
||||
path = path.substring(0, path.length() - (ZIM_EXTENSION + PART).length() + 2) + "aa";
|
||||
FileUtils.deleteZimFile(path);
|
||||
}
|
||||
} else {
|
||||
Log.i(KIWIX_TAG, "Download completed, renaming file ([" + file.getPath() + "] -> .zim)");
|
||||
file.renameTo(new File(file.getPath().replace(".part", "")));
|
||||
Log.i(KIWIX_TAG, "Download completed, renaming file ([" + file.getPath() + "] -> .zim.part)");
|
||||
file.renameTo(new File(file.getPath().replaceAll(".part$", "")));
|
||||
}
|
||||
// Mark chunk status as downloaded
|
||||
chunk.isDownloaded = true;
|
||||
@ -627,6 +659,7 @@ public class DownloadService extends Service {
|
||||
NotificationChannel ongoingDownloadsChannel = new NotificationChannel(
|
||||
Constants.ONGOING_DOWNLOAD_CHANNEL_ID, name, importance);
|
||||
ongoingDownloadsChannel.setDescription(description);
|
||||
ongoingDownloadsChannel.setSound(null, null);
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(
|
||||
NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(ongoingDownloadsChannel);
|
||||
|
@ -140,7 +140,9 @@ import static org.kiwix.kiwixmobile.utils.Constants.TAG_CURRENT_POSITIONS;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.TAG_CURRENT_TAB;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.TAG_FILE_SEARCHED;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX;
|
||||
import static org.kiwix.kiwixmobile.utils.LanguageUtils.getResourceString;
|
||||
import static org.kiwix.kiwixmobile.utils.StyleUtils.dialogStyle;
|
||||
import static org.kiwix.kiwixmobile.utils.UpdateUtils.reformatProviderUrl;
|
||||
|
||||
public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
MainContract.View, BooksAdapter.OnItemClickListener {
|
||||
@ -321,6 +323,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
presenter.attachView(this);
|
||||
new WebView(this).destroy(); // Workaround for buggy webViews see #710
|
||||
wifiOnly = sharedPreferenceUtil.getPrefWifiOnly();
|
||||
nightMode = sharedPreferenceUtil.nightMode();
|
||||
handleLocaleCheck();
|
||||
@ -346,25 +349,8 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
|
||||
tableDrawerRight.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
TableDrawerAdapter tableDrawerAdapter = new TableDrawerAdapter();
|
||||
TableDrawerAdapter tableDrawerAdapter = setupTableDrawerAdapter();
|
||||
tableDrawerRight.setAdapter(tableDrawerAdapter);
|
||||
tableDrawerAdapter.setTableClickListener(new TableClickListener() {
|
||||
@Override
|
||||
public void onHeaderClick(View view) {
|
||||
getCurrentWebView().setScrollY(0);
|
||||
drawerLayout.closeDrawer(GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSectionClick(View view, int position) {
|
||||
getCurrentWebView().loadUrl("javascript:document.getElementById('"
|
||||
+ documentSections.get(position).id
|
||||
+ "').scrollIntoView();");
|
||||
|
||||
drawerLayout.closeDrawers();
|
||||
}
|
||||
});
|
||||
|
||||
tableDrawerAdapter.notifyDataSetChanged();
|
||||
|
||||
tabsAdapter.setTabClickListener(new TabsAdapter.TabClickListener() {
|
||||
@ -391,8 +377,17 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
documentParser = new DocumentParser(new DocumentParser.SectionsListener() {
|
||||
@Override
|
||||
public void sectionsLoaded(String title, List<DocumentSection> sections) {
|
||||
for (DocumentSection section : sections) {
|
||||
if (section.title.contains("REPLACE_")) {
|
||||
section.title = getResourceString(getApplicationContext(), section.title);
|
||||
}
|
||||
}
|
||||
documentSections.addAll(sections);
|
||||
tableDrawerAdapter.setTitle(title);
|
||||
if (title.contains("REPLACE_")) {
|
||||
tableDrawerAdapter.setTitle(getResourceString(getApplicationContext(), title));
|
||||
} else {
|
||||
tableDrawerAdapter.setTitle(title);
|
||||
}
|
||||
tableDrawerAdapter.setSections(documentSections);
|
||||
tableDrawerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
@ -443,6 +438,26 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
new ItemTouchHelper(tabCallback).attachToRecyclerView(tabRecyclerView);
|
||||
}
|
||||
|
||||
private TableDrawerAdapter setupTableDrawerAdapter() {
|
||||
TableDrawerAdapter tableDrawerAdapter = new TableDrawerAdapter();
|
||||
tableDrawerAdapter.setTableClickListener(new TableClickListener() {
|
||||
@Override
|
||||
public void onHeaderClick(View view) {
|
||||
getCurrentWebView().setScrollY(0);
|
||||
drawerLayout.closeDrawer(GravityCompat.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSectionClick(View view, int position) {
|
||||
getCurrentWebView().loadUrl("javascript:document.getElementById('"
|
||||
+ documentSections.get(position).id
|
||||
+ "').scrollIntoView();");
|
||||
drawerLayout.closeDrawers();
|
||||
}
|
||||
});
|
||||
return tableDrawerAdapter;
|
||||
}
|
||||
|
||||
private void showTabSwitcher() {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.ic_round_add_white_36dp));
|
||||
@ -750,7 +765,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
}
|
||||
}
|
||||
|
||||
private KiwixWebView getCurrentWebView() {
|
||||
KiwixWebView getCurrentWebView() {
|
||||
if (webViewList.size() == 0) return newTab();
|
||||
if (currentWebViewIndex < webViewList.size()) {
|
||||
return webViewList.get(currentWebViewIndex);
|
||||
@ -1191,18 +1206,19 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
intent.setAction("");
|
||||
goToSearch(true);
|
||||
break;
|
||||
case Intent.ACTION_VIEW: {
|
||||
final String zimFile = ZimContentProvider.getZimFile();
|
||||
saveTabStates();
|
||||
Intent i = new Intent(MainActivity.this, SearchActivity.class);
|
||||
i.putExtra(EXTRA_ZIM_FILE, zimFile);
|
||||
if (intent.getData() != null) {
|
||||
i.putExtra(EXTRA_SEARCH, intent.getData().getLastPathSegment());
|
||||
case Intent.ACTION_VIEW:
|
||||
if (intent.getType() == null || !intent.getType().equals("application/octet-stream")) {
|
||||
final String zimFile = ZimContentProvider.getZimFile();
|
||||
saveTabStates();
|
||||
Intent i = new Intent(MainActivity.this, SearchActivity.class);
|
||||
i.putExtra(EXTRA_ZIM_FILE, zimFile);
|
||||
if (intent.getData() != null) {
|
||||
i.putExtra(EXTRA_SEARCH, intent.getData().getLastPathSegment());
|
||||
}
|
||||
intent.setAction("");
|
||||
startActivityForResult(i, REQUEST_FILE_SEARCH);
|
||||
}
|
||||
intent.setAction("");
|
||||
startActivityForResult(i, REQUEST_FILE_SEARCH);
|
||||
break;
|
||||
}
|
||||
case NEW_TAB:
|
||||
newTab(HOME_URL);
|
||||
break;
|
||||
@ -1602,11 +1618,11 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
|
||||
JSONArray urls = new JSONArray(zimArticles);
|
||||
JSONArray positions = new JSONArray(zimPositions);
|
||||
int i = 0;
|
||||
getCurrentWebView().loadUrl(urls.getString(i));
|
||||
getCurrentWebView().loadUrl(reformatProviderUrl(urls.getString(i)));
|
||||
getCurrentWebView().setScrollY(positions.getInt(i));
|
||||
i++;
|
||||
for (; i < urls.length(); i++) {
|
||||
newTab(urls.getString(i));
|
||||
newTab(reformatProviderUrl(urls.getString(i)));
|
||||
getCurrentWebView().setScrollY(positions.getInt(i));
|
||||
}
|
||||
selectTab(currentTab);
|
||||
|
@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.main;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
@ -45,7 +46,7 @@ public class TableDrawerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
||||
|
||||
private int selectedPosition = 0;
|
||||
|
||||
public TableDrawerAdapter() {
|
||||
TableDrawerAdapter() {
|
||||
sections = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -54,8 +55,9 @@ public class TableDrawerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
||||
return 1;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
int resource = R.layout.section_list;
|
||||
Context context = parent.getContext();
|
||||
View v = LayoutInflater.from(context).inflate(resource, parent, false);
|
||||
@ -72,7 +74,7 @@ public class TableDrawerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
ViewHolder vh = (ViewHolder) holder;
|
||||
|
||||
Context context = holder.itemView.getContext();
|
||||
@ -85,6 +87,9 @@ public class TableDrawerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
||||
vh.title.setText(title);
|
||||
} else {
|
||||
String empty = context.getString(R.string.no_section_info);
|
||||
if (context instanceof MainActivity) {
|
||||
empty = ((MainActivity) context).getCurrentWebView().getTitle();
|
||||
}
|
||||
vh.title.setText(empty);
|
||||
}
|
||||
vh.itemView.setOnClickListener(v -> {
|
||||
@ -152,14 +157,14 @@ public class TableDrawerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
||||
|
||||
public static class HeaderViewHolder extends ViewHolder {
|
||||
|
||||
public HeaderViewHolder(View v) {
|
||||
HeaderViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SectionViewHolder extends ViewHolder {
|
||||
|
||||
public SectionViewHolder(View v) {
|
||||
SectionViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import static org.kiwix.kiwixmobile.utils.DimenUtils.getToolbarHeight;
|
||||
import static org.kiwix.kiwixmobile.utils.DimenUtils.getWindowHeight;
|
||||
import static org.kiwix.kiwixmobile.utils.DimenUtils.getWindowWidth;
|
||||
import static org.kiwix.kiwixmobile.utils.ImageUtils.getBitmapFromView;
|
||||
import static org.kiwix.kiwixmobile.utils.LanguageUtils.getResourceString;
|
||||
import static org.kiwix.kiwixmobile.utils.StyleUtils.fromHtml;
|
||||
|
||||
public class TabsAdapter extends RecyclerView.Adapter<TabsAdapter.ViewHolder> {
|
||||
@ -113,7 +114,11 @@ public class TabsAdapter extends RecyclerView.Adapter<TabsAdapter.ViewHolder> {
|
||||
if (webView.getParent() != null) {
|
||||
((ViewGroup) webView.getParent()).removeView(webView);
|
||||
}
|
||||
holder.title.setText(fromHtml(webView.getTitle()));
|
||||
String webViewTitle = fromHtml(webView.getTitle()).toString();
|
||||
if (webViewTitle.contains("REPLACE_")) {
|
||||
webViewTitle = getResourceString(holder.title.getContext().getApplicationContext(), webViewTitle);
|
||||
}
|
||||
holder.title.setText(webViewTitle);
|
||||
holder.close.setOnClickListener(v -> listener.onCloseTab(v, holder.getAdapterPosition()));
|
||||
holder.content.setImageBitmap(getBitmapFromView(webView));
|
||||
holder.content.setOnClickListener(v -> {
|
||||
|
@ -29,7 +29,6 @@ import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@ -63,9 +62,10 @@ public class SearchActivity extends BaseActivity
|
||||
public static final String EXTRA_SEARCH_IN_TEXT = "bool_searchintext";
|
||||
|
||||
private final int REQ_CODE_SPEECH_INPUT = 100;
|
||||
private ListView mListView;
|
||||
private AutoCompleteAdapter mAutoAdapter;
|
||||
private ArrayAdapter<String> mDefaultAdapter;
|
||||
private ListView listView;
|
||||
private ArrayAdapter<String> currentAdapter;
|
||||
private AutoCompleteAdapter autoAdapter;
|
||||
private ArrayAdapter<String> defaultAdapter;
|
||||
private SearchView searchView;
|
||||
private String searchText;
|
||||
|
||||
@ -85,14 +85,14 @@ public class SearchActivity extends BaseActivity
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_back);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
searchPresenter.attachView(this);
|
||||
mListView = findViewById(R.id.search_list);
|
||||
mDefaultAdapter = getDefaultAdapter();
|
||||
listView = findViewById(R.id.search_list);
|
||||
defaultAdapter = getDefaultAdapter();
|
||||
searchPresenter.getRecentSearches();
|
||||
mListView.setAdapter(mDefaultAdapter);
|
||||
activateDefaultAdapter();
|
||||
|
||||
mAutoAdapter = new AutoCompleteAdapter(this);
|
||||
mListView.setOnItemClickListener(this);
|
||||
mListView.setOnItemLongClickListener(this);
|
||||
autoAdapter = new AutoCompleteAdapter(this);
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setOnItemLongClickListener(this);
|
||||
|
||||
boolean IS_VOICE_SEARCH_INTENT = getIntent().getBooleanExtra(EXTRA_IS_WIDGET_VOICE, false);
|
||||
if (IS_VOICE_SEARCH_INTENT) {
|
||||
@ -100,10 +100,20 @@ public class SearchActivity extends BaseActivity
|
||||
}
|
||||
}
|
||||
|
||||
public void activateDefaultAdapter() {
|
||||
currentAdapter = defaultAdapter;
|
||||
listView.setAdapter(currentAdapter);
|
||||
}
|
||||
|
||||
public void activateAutoAdapter() {
|
||||
currentAdapter = autoAdapter;
|
||||
listView.setAdapter(currentAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRecentSearches(List<String> recentSearches) {
|
||||
mDefaultAdapter.addAll(recentSearches);
|
||||
mDefaultAdapter.notifyDataSetChanged();
|
||||
defaultAdapter.addAll(recentSearches);
|
||||
defaultAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -128,8 +138,8 @@ public class SearchActivity extends BaseActivity
|
||||
searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
|
||||
if (searchText != null) {
|
||||
searchView.setQuery(searchText, false);
|
||||
mListView.setAdapter(mAutoAdapter);
|
||||
mAutoAdapter.getFilter().filter(searchText.toLowerCase());
|
||||
activateAutoAdapter();
|
||||
autoAdapter.getFilter().filter(searchText.toLowerCase());
|
||||
}
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
@ -140,10 +150,10 @@ public class SearchActivity extends BaseActivity
|
||||
@Override
|
||||
public boolean onQueryTextChange(String s) {
|
||||
if (s.equals("")) {
|
||||
mListView.setAdapter(mDefaultAdapter);
|
||||
activateDefaultAdapter();
|
||||
} else {
|
||||
mListView.setAdapter(mAutoAdapter);
|
||||
mAutoAdapter.getFilter().filter(s.toLowerCase());
|
||||
activateAutoAdapter();
|
||||
autoAdapter.getFilter().filter(s.toLowerCase());
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -198,15 +208,7 @@ public class SearchActivity extends BaseActivity
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
CharSequence text = ((TextView) view).getText();
|
||||
String title;
|
||||
if (text instanceof Spanned) {
|
||||
title = Html.toHtml((Spanned) text);
|
||||
// To remove the "ltr style information" from spanned text
|
||||
title = title.substring(title.indexOf(">") + 1, title.lastIndexOf("<"));
|
||||
} else {
|
||||
title = text.toString();
|
||||
}
|
||||
String title = currentAdapter.getItem(position);
|
||||
searchPresenter.saveSearch(title);
|
||||
sendMessage(title);
|
||||
}
|
||||
@ -242,8 +244,8 @@ public class SearchActivity extends BaseActivity
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (parent.getAdapter() == mDefaultAdapter) {
|
||||
String searched = mListView.getItemAtPosition(position).toString();
|
||||
if (parent.getAdapter() == defaultAdapter) {
|
||||
String searched = listView.getItemAtPosition(position).toString();
|
||||
deleteSpecificSearchDialog(searched);
|
||||
}
|
||||
return true;
|
||||
@ -268,8 +270,8 @@ public class SearchActivity extends BaseActivity
|
||||
}
|
||||
|
||||
private void resetAdapter() {
|
||||
mDefaultAdapter = getDefaultAdapter();
|
||||
mListView.setAdapter(mDefaultAdapter);
|
||||
defaultAdapter = getDefaultAdapter();
|
||||
activateDefaultAdapter();
|
||||
searchPresenter.getRecentSearches();
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ public class SplashActivity extends BaseActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!BuildConfig.DEBUG) {
|
||||
Context appContext = this;
|
||||
Context appContext = getApplicationContext();
|
||||
Thread.setDefaultUncaughtExceptionHandler((paramThread, paramThrowable) -> {
|
||||
|
||||
final Intent intent = new Intent(appContext, ErrorActivity.class);
|
||||
|
@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.utils;
|
||||
|
||||
import org.kiwix.kiwixmobile.BuildConfig;
|
||||
|
||||
public final class Constants {
|
||||
|
||||
public static final String TAG_KIWIX = "kiwix";
|
||||
@ -125,4 +127,8 @@ public final class Constants {
|
||||
// Notification Channel Constants
|
||||
public static final String ONGOING_DOWNLOAD_CHANNEL_ID = "ongoing_downloads_channel_id";
|
||||
|
||||
public static final String OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base";
|
||||
|
||||
public static final String NEW_PROVIDER_DOMAIN = BuildConfig.APPLICATION_ID + ".zim.base";
|
||||
|
||||
}
|
||||
|
@ -107,10 +107,10 @@ public class LanguageUtils {
|
||||
return mLocaleMap.get(iso3.toUpperCase());
|
||||
}
|
||||
|
||||
public static Locale getCurrentLocale(Context context){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
|
||||
public static Locale getCurrentLocale(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return context.getResources().getConfiguration().getLocales().get(0);
|
||||
} else{
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
return context.getResources().getConfiguration().locale;
|
||||
}
|
||||
@ -151,6 +151,20 @@ public class LanguageUtils {
|
||||
return "fonts/DejaVuSansCondensed.ttf";
|
||||
}
|
||||
|
||||
public static String getResourceString(Context appContext, String str) {
|
||||
String resourceName = str;
|
||||
if (resourceName.contains("REPLACE_")) {
|
||||
resourceName = resourceName.replace("REPLACE_", "");
|
||||
}
|
||||
int resourceId = appContext.getResources()
|
||||
.getIdentifier(
|
||||
resourceName,
|
||||
"string",
|
||||
appContext.getPackageName()
|
||||
);
|
||||
return appContext.getResources().getString(resourceId);
|
||||
}
|
||||
|
||||
// Read the language codes, that are supported in this app from the locales.txt file
|
||||
private void getLanguageCodesFromAssets() {
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
package org.kiwix.kiwixmobile.utils;
|
||||
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.NEW_PROVIDER_DOMAIN;
|
||||
import static org.kiwix.kiwixmobile.utils.Constants.OLD_PROVIDER_DOMAIN;
|
||||
|
||||
public class UpdateUtils {
|
||||
public static String reformatProviderUrl(String url) {
|
||||
return url.replace(OLD_PROVIDER_DOMAIN, NEW_PROVIDER_DOMAIN);
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kiwix.kiwixmobile.BuildConfig;
|
||||
import org.kiwix.kiwixmobile.downloader.ChunkUtils;
|
||||
import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book;
|
||||
|
||||
import java.io.File;
|
||||
@ -61,8 +62,8 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
public static synchronized void deleteZimFile(String path) {
|
||||
if (path.substring(path.length() - 5).equals(".part")) {
|
||||
path = path.substring(0, path.length() - 5);
|
||||
if (path.substring(path.length() - ChunkUtils.PART.length()).equals(ChunkUtils.PART)) {
|
||||
path = path.substring(0, path.length() - ChunkUtils.PART.length());
|
||||
}
|
||||
Log.i("kiwix", "Deleting file: " + path);
|
||||
File file = new File(path);
|
||||
@ -86,10 +87,16 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
private static synchronized boolean deleteZimFileParts(String path) {
|
||||
File file = new File(path + ".part");
|
||||
File file = new File(path + ChunkUtils.PART);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
return true;
|
||||
} else {
|
||||
File singlePart = new File(path + ".part");
|
||||
if (singlePart.exists()) {
|
||||
singlePart.delete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -206,15 +213,16 @@ public class FileUtils {
|
||||
stream.read(buffer);
|
||||
stream.close();
|
||||
content = new String(buffer);
|
||||
} catch (IOException ignored) { }
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
|
||||
return readCsv(content);
|
||||
}
|
||||
|
||||
public static List<File> getAllZimParts(Book book) {
|
||||
List<File> files = new ArrayList<>();
|
||||
if(book.file.getPath().endsWith(".zim") || book.file.getPath().endsWith(".zim.part")) {
|
||||
if(book.file.exists()) {
|
||||
if (book.file.getPath().endsWith(".zim") || book.file.getPath().endsWith(".zim.part")) {
|
||||
if (book.file.exists()) {
|
||||
files.add(book.file);
|
||||
} else {
|
||||
files.add(new File(book.file + ".part"));
|
||||
@ -222,12 +230,12 @@ public class FileUtils {
|
||||
return files;
|
||||
}
|
||||
String path = book.file.getPath();
|
||||
for(char alphabetFirst = 'a'; alphabetFirst <= 'z'; alphabetFirst++) {
|
||||
for(char alphabetSecond = 'a'; alphabetSecond <= 'z'; alphabetSecond++) {
|
||||
for (char alphabetFirst = 'a'; alphabetFirst <= 'z'; alphabetFirst++) {
|
||||
for (char alphabetSecond = 'a'; alphabetSecond <= 'z'; alphabetSecond++) {
|
||||
path = path.substring(0, path.length() - 2) + alphabetFirst + alphabetSecond;
|
||||
if(new File(path).exists()) {
|
||||
if (new File(path).exists()) {
|
||||
files.add(new File(path));
|
||||
} else if(new File(path + ".part").exists()) {
|
||||
} else if (new File(path + ".part").exists()) {
|
||||
files.add(new File(path + ".part"));
|
||||
} else {
|
||||
return files;
|
||||
@ -267,7 +275,7 @@ public class FileUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getFileName (String fileName) {
|
||||
public static String getFileName(String fileName) {
|
||||
if (new File(fileName).exists()) {
|
||||
return fileName;
|
||||
} else if (new File(fileName + ".part").exists()) {
|
||||
|
@ -163,11 +163,11 @@ public class ZimFileSelectFragment extends BaseFragment
|
||||
|
||||
public void checkPermissions() {
|
||||
if (ContextCompat.checkSelfPermission(zimManageActivity,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT > 18) {
|
||||
Toast.makeText(super.getActivity(), getResources().getString(R.string.request_storage), Toast.LENGTH_LONG)
|
||||
.show();
|
||||
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
REQUEST_STORAGE_PERMISSION);
|
||||
} else {
|
||||
getFiles();
|
||||
|
@ -44,6 +44,7 @@
|
||||
<item name="android:textColorPrimary">@color/primary_light</item>
|
||||
<item name="android:textColorSecondary">@color/primary_light</item>
|
||||
<item name="android:textColorTertiary">@color/primary_light</item>
|
||||
<item name="android:alertDialogTheme">@style/AppTheme.Dialog.Night</item>
|
||||
<item name="colorControlNormal">@color/accent</item>
|
||||
</style>
|
||||
|
||||
@ -79,10 +80,12 @@
|
||||
<style name="AppTheme.Dialog.Night" parent="Theme.AppCompat.DayNight.Dialog.Alert">
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="android:textColorPrimary">@color/white</item>
|
||||
<item name="android:colorBackground">@color/cardview_dark_background</item>
|
||||
<item name="buttonBarNegativeButtonStyle">@style/NegativeButtonStyle</item>
|
||||
<item name="buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item>
|
||||
<item name="buttonBarNeutralButtonStyle">@style/NeutralButtonStyle</item>
|
||||
<item name="android:textColorAlertDialogListItem">@color/white</item>
|
||||
<item name="android:windowBackground">@color/cardview_dark_background</item>
|
||||
<item name="android:textColorSecondary">@color/primary_light</item>
|
||||
</style>
|
||||
|
||||
<style name="NegativeButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
|
||||
|
@ -16,7 +16,7 @@ runID = os.environ['TRAVIS_BUILD_NUMBER']
|
||||
apiKey = os.environ['BITBAR_API_KEY']
|
||||
testName = "Auto Test {}".format(runID)
|
||||
|
||||
for x in range(0, 50):
|
||||
for x in range(0, 200):
|
||||
r = requests.get('https://cloud.testdroid.com/api/me/projects/{}/runs'.format(PROJECT_ID), auth=(apiKey, ''), headers={"Accept" : "application/json"})
|
||||
result = list(filter(lambda run: run.get("displayName") == testName, r.json().get("data")))
|
||||
if len(result) > 0 and result[0].get("state") == "FINISHED":
|
||||
|
Loading…
x
Reference in New Issue
Block a user