Merge branch 'develop' into feature/rss-manual-parsing
This commit is contained in:
commit
604ccb3544
@ -60,7 +60,7 @@ dependencies {
|
|||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
implementation 'com.google.android.material:material:1.2.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.palette:palette:1.0.0'
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
@ -71,7 +71,7 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.2.3"
|
implementation "androidx.fragment:fragment-ktx:1.2.3"
|
||||||
|
implementation "androidx.browser:browser:1.2.0"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
|
@ -29,7 +29,7 @@ public class ReadropsDebugApp extends ReadropsApp implements Configuration.Provi
|
|||||||
SoLoader.init(this, false);
|
SoLoader.init(this, false);
|
||||||
|
|
||||||
initFlipper();
|
initFlipper();
|
||||||
initNiddler();
|
//initNiddler();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initFlipper() {
|
private void initFlipper() {
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="28" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".ReadropsApp"
|
android:name=".ReadropsApp"
|
||||||
|
@ -7,7 +7,6 @@ import android.os.Bundle;
|
|||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -70,33 +69,15 @@ public class AddFeedActivity extends AppCompatActivity implements View.OnClickLi
|
|||||||
binding.addFeedOk.setOnClickListener(this);
|
binding.addFeedOk.setOnClickListener(this);
|
||||||
binding.addFeedOk.setEnabled(false);
|
binding.addFeedOk.setEnabled(false);
|
||||||
|
|
||||||
binding.addFeedTextInput.setOnTouchListener((v, event) -> {
|
|
||||||
final int DRAWABLE_RIGHT = 2;
|
|
||||||
|
|
||||||
int drawablePos = (binding.addFeedTextInput.getRight() -
|
|
||||||
binding.addFeedTextInput.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width());
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP && event.getRawX() >= drawablePos) {
|
|
||||||
binding.addFeedTextInput.setText("");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
viewModel = new ViewModelProvider(this).get(AddFeedsViewModel.class);
|
viewModel = new ViewModelProvider(this).get(AddFeedsViewModel.class);
|
||||||
|
|
||||||
parseItemsAdapter = new ItemAdapter<>();
|
parseItemsAdapter = new ItemAdapter<>();
|
||||||
fastAdapter = FastAdapter.with(parseItemsAdapter);
|
fastAdapter = FastAdapter.with(parseItemsAdapter);
|
||||||
fastAdapter.withSelectable(true);
|
fastAdapter.withSelectable(true);
|
||||||
fastAdapter.withOnClickListener((v, adapter, item, position) -> {
|
fastAdapter.withOnClickListener((v, adapter, item, position) -> {
|
||||||
if (item.isChecked()) {
|
item.setChecked(!item.isChecked());
|
||||||
item.setChecked(false);
|
|
||||||
fastAdapter.notifyAdapterItemChanged(position);
|
|
||||||
} else {
|
|
||||||
item.setChecked(true);
|
|
||||||
fastAdapter.notifyAdapterItemChanged(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fastAdapter.notifyAdapterItemChanged(position);
|
||||||
binding.addFeedOk.setEnabled(recyclerViewHasCheckedItems());
|
binding.addFeedOk.setEnabled(recyclerViewHasCheckedItems());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -22,6 +22,7 @@ import android.webkit.WebView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.ShareCompat;
|
import androidx.core.app.ShareCompat;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@ -199,12 +200,7 @@ public class ItemActivity extends AppCompatActivity {
|
|||||||
shareArticle();
|
shareArticle();
|
||||||
return true;
|
return true;
|
||||||
case R.id.item_open:
|
case R.id.item_open:
|
||||||
int value = Integer.parseInt(SharedPreferencesManager.readString(this,
|
openUrl();
|
||||||
SharedPreferencesManager.SharedPrefKey.OPEN_ITEMS_IN));
|
|
||||||
if (value == 0)
|
|
||||||
openInNavigator();
|
|
||||||
else
|
|
||||||
openInWebView();
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@ -217,6 +213,22 @@ public class ItemActivity extends AppCompatActivity {
|
|||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openUrl() {
|
||||||
|
int value = Integer.parseInt(SharedPreferencesManager.readString(this,
|
||||||
|
SharedPreferencesManager.SharedPrefKey.OPEN_ITEMS_IN));
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
openInNavigator();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
openInWebView();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
openInCustomTab();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void openInNavigator() {
|
private void openInNavigator() {
|
||||||
Intent urlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(itemWithFeed.getItem().getLink()));
|
Intent urlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(itemWithFeed.getItem().getLink()));
|
||||||
startActivity(urlIntent);
|
startActivity(urlIntent);
|
||||||
@ -225,11 +237,27 @@ public class ItemActivity extends AppCompatActivity {
|
|||||||
private void openInWebView() {
|
private void openInWebView() {
|
||||||
Intent intent = new Intent(this, WebViewActivity.class);
|
Intent intent = new Intent(this, WebViewActivity.class);
|
||||||
intent.putExtra(WEB_URL, itemWithFeed.getItem().getLink());
|
intent.putExtra(WEB_URL, itemWithFeed.getItem().getLink());
|
||||||
intent.putExtra(ACTION_BAR_COLOR, itemWithFeed.getColor() != 0 ? itemWithFeed.getColor() : itemWithFeed.getBgColor());
|
intent.putExtra(ACTION_BAR_COLOR, itemWithFeed.getBgColor() != 0 ? itemWithFeed.getBgColor() : itemWithFeed.getColor());
|
||||||
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openInCustomTab() {
|
||||||
|
boolean darkTheme = Boolean.parseBoolean(SharedPreferencesManager.readString(this, SharedPreferencesManager.SharedPrefKey.DARK_THEME));
|
||||||
|
int color = itemWithFeed.getBgColor() != 0 ? itemWithFeed.getBgColor() : itemWithFeed.getColor();
|
||||||
|
|
||||||
|
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder()
|
||||||
|
.addDefaultShareMenuItem()
|
||||||
|
.setToolbarColor(color)
|
||||||
|
.setSecondaryToolbarColor(color)
|
||||||
|
.setColorScheme(darkTheme ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT)
|
||||||
|
.enableUrlBarHiding()
|
||||||
|
.setShowTitle(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
customTabsIntent.launchUrl(this, Uri.parse(itemWithFeed.getItem().getLink()));
|
||||||
|
}
|
||||||
|
|
||||||
private void shareArticle() {
|
private void shareArticle() {
|
||||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||||
shareIntent.setType("text/plain");
|
shareIntent.setType("text/plain");
|
||||||
|
@ -7,10 +7,9 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -29,6 +28,7 @@ import com.readrops.app.ReadropsApp;
|
|||||||
import com.readrops.app.activities.AddAccountActivity;
|
import com.readrops.app.activities.AddAccountActivity;
|
||||||
import com.readrops.app.activities.ManageFeedsFoldersActivity;
|
import com.readrops.app.activities.ManageFeedsFoldersActivity;
|
||||||
import com.readrops.app.activities.NotificationPermissionActivity;
|
import com.readrops.app.activities.NotificationPermissionActivity;
|
||||||
|
import com.readrops.app.utils.FileUtils;
|
||||||
import com.readrops.app.utils.PermissionManager;
|
import com.readrops.app.utils.PermissionManager;
|
||||||
import com.readrops.app.utils.SharedPreferencesManager;
|
import com.readrops.app.utils.SharedPreferencesManager;
|
||||||
import com.readrops.app.utils.Utils;
|
import com.readrops.app.utils.Utils;
|
||||||
@ -36,14 +36,10 @@ import com.readrops.app.viewmodels.AccountViewModel;
|
|||||||
import com.readrops.db.entities.account.Account;
|
import com.readrops.db.entities.account.Account;
|
||||||
import com.readrops.db.entities.account.AccountType;
|
import com.readrops.db.entities.account.AccountType;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.observers.DisposableCompletableObserver;
|
import io.reactivex.observers.DisposableCompletableObserver;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import kotlin.Unit;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
import static com.readrops.api.opml.OPMLHelper.OPEN_OPML_FILE_REQUEST;
|
import static com.readrops.api.opml.OPMLHelper.OPEN_OPML_FILE_REQUEST;
|
||||||
@ -77,6 +73,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
addPreferencesFromResource(R.xml.acount_preferences);
|
addPreferencesFromResource(R.xml.acount_preferences);
|
||||||
@ -121,16 +118,7 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
opmlPref.setOnPreferenceClickListener(preference -> {
|
opmlPref.setOnPreferenceClickListener(preference -> {
|
||||||
new MaterialDialog.Builder(getActivity())
|
new MaterialDialog.Builder(getActivity())
|
||||||
.items(R.array.opml_import_export)
|
.items(R.array.opml_import_export)
|
||||||
.itemsCallback(((dialog, itemView, position, text) -> {
|
.itemsCallback(((dialog, itemView, position, text) -> openOPMLMode(position)))
|
||||||
if (position == 0) {
|
|
||||||
OPMLHelper.openFileIntent(this);
|
|
||||||
} else {
|
|
||||||
if (PermissionManager.isPermissionGranted(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
|
||||||
exportAsOPMLFile();
|
|
||||||
else
|
|
||||||
requestExternalStoragePermission();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.show();
|
.show();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -179,6 +167,22 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openOPMLMode(int position) {
|
||||||
|
if (position == 0) {
|
||||||
|
OPMLHelper.openFileIntent(this);
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||||
|
if (PermissionManager.isPermissionGranted(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
exportAsOPMLFile();
|
||||||
|
} else {
|
||||||
|
requestExternalStoragePermission();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exportAsOPMLFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// region opml import
|
// region opml import
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -231,51 +235,34 @@ public class AccountSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
//region opml export
|
//region opml export
|
||||||
|
|
||||||
private void exportAsOPMLFile() {
|
private void exportAsOPMLFile() {
|
||||||
|
String fileName = "subscriptions.opml";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
|
String path = FileUtils.writeDownloadFile(getContext(), fileName, "text/xml", outputStream -> {
|
||||||
File file = new File(filePath, "subscriptions.opml");
|
viewModel.getFoldersWithFeeds()
|
||||||
|
.flatMapCompletable(folderListMap -> OPMLParser.write(folderListMap, outputStream))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnError(e -> Utils.showSnackbar(getView(), e.getMessage()))
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
final OutputStream outputStream = new FileOutputStream(file);
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
|
||||||
viewModel.getFoldersWithFeeds()
|
displayNotification(fileName, path);
|
||||||
.flatMapCompletable(folderListMap -> OPMLParser.write(folderListMap, outputStream))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doAfterTerminate(() -> {
|
|
||||||
try {
|
|
||||||
outputStream.flush();
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
Utils.showSnackbar(getView(), e.getMessage());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.subscribe(new DisposableCompletableObserver() {
|
|
||||||
@Override
|
|
||||||
public void onComplete() {
|
|
||||||
displayNotification(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable e) {
|
|
||||||
Utils.showSnackbar(getView(), e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.getMessage());
|
displayErrorMessage();
|
||||||
Utils.showSnackbar(getView(), e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayNotification(File file) {
|
private void displayNotification(String name, String absolutePath) {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setDataAndType(Uri.parse(file.getAbsolutePath()), "text/plain");
|
intent.setDataAndType(Uri.parse(absolutePath), "text/plain");
|
||||||
|
|
||||||
Notification notification = new NotificationCompat.Builder(getContext(), ReadropsApp.OPML_EXPORT_CHANNEL_ID)
|
Notification notification = new NotificationCompat.Builder(getContext(), ReadropsApp.OPML_EXPORT_CHANNEL_ID)
|
||||||
.setContentTitle(getString(R.string.opml_export))
|
.setContentTitle(getString(R.string.opml_export))
|
||||||
.setContentText(file.getName())
|
.setContentText(name)
|
||||||
.setSmallIcon(R.drawable.ic_notif)
|
.setSmallIcon(R.drawable.ic_notif)
|
||||||
.setContentIntent(PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
.setContentIntent(PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
|
69
app/src/main/java/com/readrops/app/utils/FileUtils.kt
Normal file
69
app/src/main/java/com/readrops/app/utils/FileUtils.kt
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package com.readrops.app.utils
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
object FileUtils {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun writeDownloadFile(context: Context, fileName: String, mimeType: String, listener: (OutputStream) -> Unit): String {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
|
writeFileApi29(context, fileName, mimeType, listener)
|
||||||
|
else
|
||||||
|
writeFileApi28(fileName, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
|
private fun writeFileApi29(context: Context, fileName: String, mimeType: String, listener: (OutputStream) -> Unit): String {
|
||||||
|
val resolver = context.contentResolver
|
||||||
|
val downloadsUri = MediaStore.Downloads
|
||||||
|
.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
|
||||||
|
val fileDetails = ContentValues().apply {
|
||||||
|
put(MediaStore.Downloads.DISPLAY_NAME, fileName)
|
||||||
|
put(MediaStore.Downloads.IS_PENDING, 1)
|
||||||
|
put(MediaStore.Downloads.MIME_TYPE, mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentUri = resolver.insert(downloadsUri, fileDetails)
|
||||||
|
|
||||||
|
resolver.openFileDescriptor(contentUri!!, "w", null).use { pfd ->
|
||||||
|
val outputStream = FileOutputStream(pfd?.fileDescriptor!!)
|
||||||
|
|
||||||
|
try {
|
||||||
|
listener(outputStream)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
outputStream.flush()
|
||||||
|
outputStream.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDetails.clear()
|
||||||
|
fileDetails.put(MediaStore.Downloads.IS_PENDING, 0)
|
||||||
|
resolver.update(contentUri, fileDetails, null, null)
|
||||||
|
|
||||||
|
return contentUri.path!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeFileApi28(fileName: String, listener: (OutputStream) -> Unit): String {
|
||||||
|
val filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath
|
||||||
|
val file = File(filePath, fileName)
|
||||||
|
|
||||||
|
val outputStream = FileOutputStream(file)
|
||||||
|
listener(outputStream)
|
||||||
|
|
||||||
|
outputStream.flush()
|
||||||
|
outputStream.close()
|
||||||
|
|
||||||
|
return file.absolutePath
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
package com.readrops.app.utils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.bumptech.glide.module.AppGlideModule;
|
|
||||||
|
|
||||||
@com.bumptech.glide.annotation.GlideModule
|
|
||||||
public class GlideModule extends AppGlideModule {
|
|
||||||
|
|
||||||
}
|
|
@ -15,6 +15,7 @@ import org.jsoup.select.Elements;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -33,25 +34,30 @@ public final class HtmlParser {
|
|||||||
* @param url url to request
|
* @param url url to request
|
||||||
* @return a list of rss urls with their title
|
* @return a list of rss urls with their title
|
||||||
*/
|
*/
|
||||||
public static List<ParsingResult> getFeedLink(String url) throws Exception {
|
public static List<ParsingResult> getFeedLink(String url) {
|
||||||
List<ParsingResult> results = new ArrayList<>();
|
List<ParsingResult> results = new ArrayList<>();
|
||||||
|
|
||||||
Document document = Jsoup.parse(getHTMLHeadFromUrl(url), url);
|
String head = getHTMLHeadFromUrl(url);
|
||||||
|
if (head != null) {
|
||||||
|
Document document = Jsoup.parse(head, url);
|
||||||
|
|
||||||
Elements elements = document.select("link");
|
Elements elements = document.select("link");
|
||||||
|
|
||||||
for (Element element : elements) {
|
for (Element element : elements) {
|
||||||
String type = element.attributes().get("type");
|
String type = element.attributes().get("type");
|
||||||
|
|
||||||
if (isTypeRssFeed(type)) {
|
if (isTypeRssFeed(type)) {
|
||||||
String feedUrl = element.absUrl("href");
|
String feedUrl = element.absUrl("href");
|
||||||
String label = element.attributes().get("title");
|
String label = element.attributes().get("title");
|
||||||
|
|
||||||
results.add(new ParsingResult(feedUrl, label));
|
results.add(new ParsingResult(feedUrl, label));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTypeRssFeed(String type) {
|
private static boolean isTypeRssFeed(String type) {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.readrops.app.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.Registry
|
||||||
|
import com.bumptech.glide.annotation.GlideModule
|
||||||
|
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
|
||||||
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
|
import com.bumptech.glide.module.AppGlideModule
|
||||||
|
import com.readrops.api.utils.HttpManager
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
class ReadropsGlideModule : AppGlideModule() {
|
||||||
|
|
||||||
|
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||||
|
val factory = OkHttpUrlLoader.Factory(HttpManager.getInstance().okHttpClient)
|
||||||
|
|
||||||
|
glide.registry.replace(GlideUrl::class.java, InputStream::class.java, factory)
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,7 @@
|
|||||||
android:id="@+id/add_feed_input_layout"
|
android:id="@+id/add_feed_input_layout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
app:layout_constraintEnd_toStartOf="@id/add_feed_load"
|
app:layout_constraintEnd_toStartOf="@id/add_feed_load"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
@ -44,7 +45,6 @@
|
|||||||
android:id="@+id/add_feed_text_input"
|
android:id="@+id/add_feed_text_input"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawableEnd="@drawable/ic_cancel_grey"
|
|
||||||
android:hint="@string/feed_url"
|
android:hint="@string/feed_url"
|
||||||
android:inputType="text" />
|
android:inputType="text" />
|
||||||
|
|
||||||
|
@ -132,5 +132,6 @@
|
|||||||
<string name="show_caption">Afficher la légende</string>
|
<string name="show_caption">Afficher la légende</string>
|
||||||
<string name="password_helper">Votre mot de passe d\'API (Configuration > Profil)</string>
|
<string name="password_helper">Votre mot de passe d\'API (Configuration > Profil)</string>
|
||||||
<string name="synchronize">Synchroniser</string>
|
<string name="synchronize">Synchroniser</string>
|
||||||
|
<string name="navigator_view">Vue navigateur</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -33,11 +33,13 @@
|
|||||||
<string-array name="open_items_in">
|
<string-array name="open_items_in">
|
||||||
<item>@string/external_navigator</item>
|
<item>@string/external_navigator</item>
|
||||||
<item>@string/webview</item>
|
<item>@string/webview</item>
|
||||||
|
<item>@string/navigator_view</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="open_item_in_values">
|
<string-array name="open_item_in_values">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="themes">
|
<string-array name="themes">
|
||||||
|
@ -138,4 +138,5 @@
|
|||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
<string name="show_caption">Show caption</string>
|
<string name="show_caption">Show caption</string>
|
||||||
<string name="synchronize">Synchronize</string>
|
<string name="synchronize">Synchronize</string>
|
||||||
|
<string name="navigator_view">Navigator view</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
android:title="@string/reload_feeds_colors" />
|
android:title="@string/reload_feeds_colors" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="0"
|
android:defaultValue="2"
|
||||||
android:entries="@array/open_items_in"
|
android:entries="@array/open_items_in"
|
||||||
android:entryValues="@array/open_item_in_values"
|
android:entryValues="@array/open_item_in_values"
|
||||||
android:key="open_items_in"
|
android:key="open_items_in"
|
||||||
|
@ -11,6 +11,7 @@ android.useAndroidX=true
|
|||||||
org.gradle.jvmargs=-Xmx1536m
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
android.databinding.incremental=true
|
android.databinding.incremental=true
|
||||||
kapt.incremental.apt=true
|
kapt.incremental.apt=true
|
||||||
|
org.gradle.parallel=true
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
Loading…
x
Reference in New Issue
Block a user