Help enabling downloads through webview (Kotlin) - Android Studio

Good day. I'm new to android development and I'm trying to develop a simple webview application, picked a nice template and went through the steps and made good progress, I managed to load my site fully and enable javascript, that works as intended, however I'm not able to make the app download anything, I host a few pdf files that should open or download through it, but nothing happens.
I looked at a few answers here and it is to my understanding that I need to specifically add a function for that, could you give me a hand? I have tried multiple different code and tweaking them, but I wasn't able to get it to work, here is my base code:
Code:
package com.logista.test.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.app.DownloadManager
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.logista.test.R
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
val myWebView: WebView = root.findViewById(R.id.webview)
myWebView.webViewClient = WebViewClient()
myWebView.settings.javaScriptEnabled = true
myWebView.loadUrl("https://www.example.org/")
return root
}
}
I believe I should be adding
Code:
import android.app.DownloadManager
And tweak this
Code:
// Set web view download listener
web_view.setDownloadListener(DownloadListener {
url,
userAgent,
contentDescription,
mimetype,
contentLength ->
// Initialize download request
val request = DownloadManager.Request(Uri.parse(url))
// Get the cookie
val cookies = CookieManager.getInstance().getCookie(url)
// Add the download request header
request.addRequestHeader("Cookie",cookies)
request.addRequestHeader("User-Agent",userAgent)
// Set download request description
request.setDescription("Downloading requested file....")
// Set download request mime tytpe
request.setMimeType(mimetype)
// Allow scanning
request.allowScanningByMediaScanner()
// Download request notification setting
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// Guess the file name
val fileName = URLUtil.guessFileName(url, contentDescription, mimetype)
// Set a destination storage for downloaded file
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
// Set request title
request.setTitle(URLUtil.guessFileName(url, contentDescription, mimetype));
// DownloadManager request more settings
request.setAllowedOverMetered(true)
request.setAllowedOverRoaming(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
request.setRequiresCharging(false)
request.setRequiresDeviceIdle(false)
}
request.setVisibleInDownloadsUi(true)
// Get the system download service
val dManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
// Finally, request the download to system download service
dManager.enqueue(request)
})
// Load button click listener
button_load.setOnClickListener{
// Load url in a web view
web_view.loadUrl(url)
}
}
Taken from here: https://android--code.blogspot.com/2018/03/android-kotlin-webview-file-download.html
I did the basics, renaming the function accordingly and such, but it gives me quite a few errors when building the app, for instance uri isn't defined, cookiemanager isn't defined, environment isn't defined, build isn't defined, and such, could you give me some guidance?

Related

Android Code: Downloading from within an app

I've searched the android website but I cannot find any sample code that will allow you to download something from a webpage/website. I'm trying to create a simple application that will allow you to download a podcast from a website straight onto the phone's sdcard. Any help is appreciated. Thanks!
[Edit]: I've found http://developer.android.com/reference/android/webkit/DownloadListener.html it seems right but I'm still a beginner and not sure how to apply the code / modify it and where to place it.
Networking is a big series of tests. Just about everything needs to be within a try/catch block, and you need to think of and test every possible contingency. Without doing that, you WILL get crashes and ugliness.
It is not the simplest thing for a newb to implement.
The SECOND thing you need to note is that WEBKIT is not what you want to mess with for *pure downloads*. If you just want to pull a file and do something manual with it, you do NOT want to be messing with an html rendering engine (that's what webkit is...).
Third, network requests should be done in a SEPARATE THREAD from the UI, otherwise it will result in a terrible user experience, ANR's, and general appearance of FREEZUPS.
You can try some of this:
Code:
public class HTTPGetData {
private byte[] data;
public HTTPGetData(){
}
public byte[] toByteArray(){
return data;
}
public void getViaHttpConnection(String url) throws IOException{
HttpClient httpClient = new DefaultHttpClient();
Log.d("***HTTPGetData",url);
HttpGet request = new HttpGet(url);
request.addHeader("Accept-Encoding", "gzip");
HttpResponse response;
try {
response = httpClient.execute(request);
boolean gzip = false;
if (response.containsHeader("Content-Encoding")){
Header[] headers = response.getHeaders("Content-Encoding");
for (int i=0; i<headers.length; i++){
if (headers[i].getValue().compareToIgnoreCase("gzip")==0) gzip = true;
}
}
int status = response.getStatusLine().getStatusCode();
// we assume that the response body contains the error message
if (status != HttpStatus.SC_OK) {
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
response.getEntity().writeTo(ostream);
Log.e("HTTP_CLIENT", ostream.toString());
throw new IOException("HTTP response code: " + status);
} else {
int len = (int)response.getEntity().getContentLength();
InputStream content;
if (gzip) content = new GZIPInputStream(response.getEntity().getContent());
else content = response.getEntity().getContent();
if (len>0 && !gzip){
byte[] theData = new byte[len];
int rec = 0;
int cread = 0;
boolean fail=false;
while (rec < len){
if ((cread=content.read(theData, rec, len-rec))==0){
Log.e("HTTP_CLIENT","Short");
fail=true;
break;
}
rec+=cread;
}
if (!fail) data=theData;
} else {
int ch;
ByteVector bv = new ByteVector(1000);
while ((ch = content.read()) != -1){
bv.add((byte)ch);
}
data = bv.toByteArray();
}
content.close(); // this will also close the connection
}
} catch (ClientProtocolException e) {
Log.e("HTTP_CLIENT","ClientProtocolException");
throw new IOException("ClientProtocolException caught in HTTPGetData.getViaHttpConnection(String url)");
} catch (IOException e) {
Log.e("HTTP_CLIENT","IOException");
throw new IOException("IOException caught in HTTPGetData.getViaHttpConnection(String url)");
}
}
}
What you can do with that is something like this;
try{
HTTPGetData hgd = new HTTPGetData();
hgd.getViaHttpConnection("http://someurl");
//open file and dump in hgd.toByteArray();
catch(IOException e){
//do something with e
}
... and of course, put that in some new thread and while its running, make something spin to give the user the impression of progress.
Thanks lb. I don't really understand perfectly but I will look up the stuff I do not understand. Thanks for the help

XML parsing-best method, general discussion

I am relatively new to programing and have been teaching myself as I go. I am trying to now integrate ESPN's API into my app which outputs XML data. I have done enough research to understand that I need to use an XML parser and that the XML can be displayed as a list view or web view by adding HTML markup.
I would like this thread to become a general discussion for all new developers on what XML parsing is and what it does. How it works, the code behind it etc.
I am currently trying to modify a code snippet from the developer.google.com website.
Android includes built in libraries for XML Parsing.
You can see a minimal use of the library here:
Code:
import java.io.IOException;
import java.io.StringReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
public class SimpleXmlPullApp
{
public static void main (String args[])
throws XmlPullParserException, IOException
{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
System.out.println("Start tag "+xpp.getName());
} else if(eventType == XmlPullParser.END_TAG) {
System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
System.out.println("End document");
}
}
So essentially, you can grab the xml from ESPN, save it to a file, parse it with that library, and output as desired.
Full details here: http://developer.android.com/reference/org/xmlpull/v1/XmlPullParser.html
the fastest way I know is the parser written in c/c++ and compiled with ndk.
bitpushr said:
Android includes built in libraries for XML Parsing.
So essentially, you can grab the xml from ESPN, save it to a file, parse it with that library, and output as desired.
Full details here: http://developer.android.com/reference/org/xmlpull/v1/XmlPullParser.html
Click to expand...
Click to collapse
I will have to try out this code. Because I am learning as I go there are many things in java I don't yet understand which ultimately make it a lot harder to accomplish tasks but I am slowly catching on
If you want to do this in a .NET (Windows x86/64, Windows Mobile, Windows Phone) environment, or a Linux MONO environment then the following will carry out the bare minimum of parsing.
Assuming you have the data in an XML file :- PARAMS.XML in the same folder as the program, created similar to this:
Code:
<?xml version="1.0" encoding="UTF-8" ?>
<PARAMS>
<Library>TESTLIB</Library>
<Source>C:\RetData\</Source>
</PARAMS>
Then the following C# code will read and extract the parameters:
Code:
using System.Xml;
string LibName,RetDir;
XmlReader xrdr = XmlReader.Create(Application.StartupPath+"\\PARAMS.xml");
xrdr.ReadToFollowing("Library");
Libname=xrdr.ReadElementContentAsString();
xrdr.ReadToFollowing("Source");
RetDir = xrdr.ReadElementContentAsString();
xrdr.Close();
After executing this code then:
LibName = "TESTLIB"
RetDir = "C:\RetData\"
CAVEAT:
I mentioned that this is the bare minimum. An XmlReader object is a forward only reader. Also, this code has no error checking.
In this case the elements must be read in the order they occur or the reader will encounter a null element and throw an error.

[Q] Impossible to hook a method in com.android.vending

Some week ago i posted into the request and index thread for set up a request to develop a module that will eliminate the need to have a certain amount of free space when you install app from the Play Store.
After some days i disassembled the play store apk [using APk_OneClick ] and i found the information and method i needed and wrote down into the forum, here my reply:
MonoS94 said:
Ok, i dig a bit into the play store apk for the 4.9.13 version in
Code:
com/google/android/finsky/download/
there is a class named
Code:
Storage
with two interesting method
Code:
dataPartitionAvailableSpace()
and
Code:
externalStorageAvailableSpace()
This two method return a long representing the available space into a particular partition using this formula [found in the partitionAvailable() method]
Code:
for (long l = localStatFs.getAvailableBytes(); ; l = localStatFs.getBlockSize() * localStatFs.getAvailableBlocks())
So hooking and replacing this two modules with a dummy version returning always full space we will defeat this annoying oddity, also being part of the play store apk i think that this will not affect any other app.
I hope that with this information someone will make a module.
Click to expand...
Click to collapse
I waited some days and about a week ago i download the ADT, set up the environment, learned a bit how to code for Android and Xposed and wrote down some code
Code:
package com.MonoS.FreeSpaceForPlayStore;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class Main implements IXposedHookLoadPackage {
private static final String GOOGLE_PLAYSTORE = "com.android.vending";
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.contains(GOOGLE_PLAYSTORE))
return;
XposedBridge.log("I'm in bro ;)" + lpparam.packageName);
findAndHookMethod("com.google.android.finsky.download.Storage", lpparam.classLoader, "dataPartitionAvailableSpace", XC_MethodReplacement.returnConstant(1073741824));
}
}
Compile the code, install on the phone, activate and reboot and the module don't work, it enter into the package but then throw me an ClassNotFoundError exception.
Then checked the name of all the method but they are right.
Then why can't my module hook the method??
Thanks for the attention

Changes applied only after reboot

Hey.
I have a little problem with my module. I can't dynamicly change settings, changes are applied only after reboot
Here code (in this case is code responding to cahnge color )
ColorPickerSample.java
Code:
String mColor = ([COLOR=#008000][B]"#" [/B][/COLOR]+ [COLOR=#660e7a][B]editText[/B][/COLOR].getText().toString());
[COLOR=#808000]@SuppressWarnings[/COLOR]([COLOR=#008000][B]"deprecation"[/B][/COLOR])
SharedPreferences pref = getSharedPreferences([COLOR=#008000][B]"pref"[/B][/COLOR], Context.[COLOR=#660e7a][B][I]MODE_WORLD_READABLE[/I][/B][/COLOR]);
Editor editor = pref.edit();
[COLOR=#808080][I]//write prefs
[/I][/COLOR]editor.putString(PrefKeys.UI_COLOR, mColor);
[COLOR=#808080][I]//apply
[/I][/COLOR]editor.commit();
Module.java
Code:
XSharedPreferences [COLOR=#660e7a][B]pref [/B][/COLOR]= [COLOR=#000080][B]new [/B][/COLOR]XSharedPreferences(ModuleTest.[COLOR=#000080][B]class[/B][/COLOR].getPackage().getName(), [COLOR=#008000][B]"pref"[/B][/COLOR]);
[COLOR=#000080][B]final [/B][/COLOR]String [COLOR=#660e7a][B]uicolor [/B][/COLOR]= [COLOR=#660e7a][B]pref[/B][/COLOR].getString(PrefKeys.[COLOR=#660e7a][I]UI_COLOR[/I][/COLOR], [COLOR=#008000][B]"#ff008080"[/B][/COLOR]);
[COLOR=#000080][B]final int [/B][/COLOR][COLOR=#660e7a][B]colorint [/B][/COLOR]= Color.[I]parseColor[/I]([COLOR=#660e7a][B]uicolor[/B][/COLOR]);
[COLOR=#808000]@Override
[/COLOR][COLOR=#000080][B]public void [/B][/COLOR]initZygote(StartupParam startupParam) [COLOR=#000080][B]throws [/B][/COLOR]Throwable {
XResources.[I]setSystemWideReplacement[/I]([COLOR=#008000][B]"android"[/B][/COLOR], [COLOR=#008000][B]"color"[/B][/COLOR], [COLOR=#008000][B]"holo_blue_light"[/B][/COLOR], Color.[I]parseColor[/I](uicolor));
}
Thank you so much for any help!
KuaQ said:
Hey.
I have a little problem with my module. I can't dynamicly change settings, changes are applied only after reboot
Here code (in this case is code responding to cahnge color )
ColorPickerSample.java
Code:
String mColor = ([COLOR=#008000][B]"#" [/B][/COLOR]+ [COLOR=#660e7a][B]editText[/B][/COLOR].getText().toString());
[COLOR=#808000]@SuppressWarnings[/COLOR]([COLOR=#008000][B]"deprecation"[/B][/COLOR])
SharedPreferences pref = getSharedPreferences([COLOR=#008000][B]"pref"[/B][/COLOR], Context.[COLOR=#660e7a][B][I]MODE_WORLD_READABLE[/I][/B][/COLOR]);
Editor editor = pref.edit();
[COLOR=#808080][I]//write prefs
[/I][/COLOR]editor.putString(PrefKeys.UI_COLOR, mColor);
[COLOR=#808080][I]//apply
[/I][/COLOR]editor.commit();
Module.java
Code:
XSharedPreferences [COLOR=#660e7a][B]pref [/B][/COLOR]= [COLOR=#000080][B]new [/B][/COLOR]XSharedPreferences(ModuleTest.[COLOR=#000080][B]class[/B][/COLOR].getPackage().getName(), [COLOR=#008000][B]"pref"[/B][/COLOR]);
[COLOR=#000080][B]final [/B][/COLOR]String [COLOR=#660e7a][B]uicolor [/B][/COLOR]= [COLOR=#660e7a][B]pref[/B][/COLOR].getString(PrefKeys.[COLOR=#660e7a][I]UI_COLOR[/I][/COLOR], [COLOR=#008000][B]"#ff008080"[/B][/COLOR]);
[COLOR=#000080][B]final int [/B][/COLOR][COLOR=#660e7a][B]colorint [/B][/COLOR]= Color.[I]parseColor[/I]([COLOR=#660e7a][B]uicolor[/B][/COLOR]);
[COLOR=#808000]@Override
[/COLOR][COLOR=#000080][B]public void [/B][/COLOR]initZygote(StartupParam startupParam) [COLOR=#000080][B]throws [/B][/COLOR]Throwable {
XResources.[I]setSystemWideReplacement[/I]([COLOR=#008000][B]"android"[/B][/COLOR], [COLOR=#008000][B]"color"[/B][/COLOR], [COLOR=#008000][B]"holo_blue_light"[/B][/COLOR], Color.[I]parseColor[/I](uicolor));
}
Thank you so much for any help!
Click to expand...
Click to collapse
Could you make broadcast receiver to get the color and set it
Sent from my SM-G530H using XDA Free mobile app
Problem solved. Thank you
initZygote's code runs only once, when system boots. So even if you change pref, nothing will happen.

[DexKit] Xposed Module Jni Lib | An easy-to-use, high-performance dex deobfuscation library

Library: DexKit
Project Link: https://github.com/LuckyPray/DexKit
About: An easy-to-use, high-performance dex deobfuscation library. Easy to use your CMAKE/Android projects.
Highlight:
JNI multi-threaded processing Dex bytecode, more efficient than JVM implementation.
It single search is ms level, You can even inject the host application at runtime without causing an ANR, to the extent permitted.
You can use it to handle reinforced apps(use ClassLoader cookies).
API introduction​There are two APIs can meet most of your usage scenarios:
DexKit::BatchFindClassesUsingStrings
DexKit::BatchFindMethodsUsingStrings
Note: In all cases you should avoid searching for keywords that contain duplicate content, eg: {"key_word", "word"}, as this will cause tags to be overwritten, resulting in inaccurate search results. If there is such a need, open the advanced search mode as much as possible, and use the string to match the content exactly, for example, modify it to this: {"^key_word$", "^word$"}
Click to expand...
Click to collapse
And there are many other APIs:
DexKit::FindMethodCaller: find caller for specified method.
DexKit::FindMethodInvoking: find the called method
DexKit::FindMethodUsingField: Find method to get/set specified field
DexKit::FindMethodUsingString: find method used utf8 string
DexKit::FindMethod: find method by multiple conditions
DexKit::FindSubClasses: find all direct subclasses of the specified class
DexKit::FindMethodUsingOpPrefixSeq: find all method using opcode prefix sequence(op range: 0x00-0xFF)
DexKit::FindMethodUsingOpCodeSeq: find all method using opcode sequence(op range: 0x00-0xFF)
DexKit::GetMethodOpCodeSeq: get method opcode sequence(op range: 0x00-0xFF)
Note: At present, all instructions are only for standard dex instructions and do not include odex optimization instructions.
Click to expand...
Click to collapse
For more detailed instructions, please refer to dex_kit.h.
Quick start​However, this approach will import an extra so file. If you don't want to import an extra so file, please use the second/third method.
build.gradle:
Code:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
app/build.gradle:
Code:
dependencies {
implementation 'com.github.LuckyPray:DexKit:<version>'
}
​JAVA Example​DexKitBridge provides 2 factory methods to create Dexkit instances:
DexKitBridge.create(apkPath): normally, please use it.
DexKitBridge.create(classLoader, true): for reinforced apps, used classLoader and set option useCookieDexFile to true.
PS: DexKitBridge.create(classLoader, useCookieDexFile = false) ≈ DexKitBridge.create(apkPath), but the former may contain part of the system dex.
Java:
import io.luckypry.dexkit.DexKitBridge;
// ...
public class DexUtil {
static {
System.loadLibrary("dexkit");
}
public static void findMethod() {
// for no-reinforced apps please use apkpath to load, because of the exist of dex2oat and CompactDex(cdex),
// dexkit currently only handles StandardDex.
String apkPath = application.applicationInfo.sourceDir
// try-with-resources, auto close DexKitBridge, no need to call DexKitBridge.close()
// if you don't use try-with-resources, be sure to manually call DexKitBridge.close() to release the jni memory
try (DexKitBridge dexKitBridge = DexKitBridge.create(apkPath)) {
if (dexKitBridge == null) {
Log.e("DexUtil", "DexKitBridge create failed");
return;
}
List<DexClassDescriptor> classes = dexKitBridge.findSubClasses("android.app.Activity", null);
for (DexClassDescriptor clazz : classes) {
String name = clazz.getName();
String simpleName = clazz.getSimpleName();
Class<?> clz = clazz.getClassInstance(hostClassLoader);
Log.i("DexUtil", "findSubClasses: " + clz);
}
} catch (Throwable e) {
Log.e("DexUtil", Log.getStackTraceString(e));
}
}
}
If you have problems using it, please create an issue on the github repo.
Reserved

Categories

Resources