Commit d232b478 authored by Sharaf Zaman's avatar Sharaf Zaman

[Android] Added android manifest and src files

ConfigsManager class helps to copy the default configurations
from assets to internal storage, where C++ files can access
them. Copying methods are only called when app is run for the
first time or after cache is cleared.
parent f49edc2a
<?xml version='1.0' encoding='utf-8'?>
<manifest package="org.kde.krita" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="krita">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name="org.krita.android.MainActivity"
android:label="krita"
android:screenOrientation="unspecified"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="krita"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:plugins/mediaservice/libqtmedia_android.so"/>
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidBearer.jar:jar/QtMultimedia.jar"/>
<meta-data android:name="android.app.static_init_classes" android:value="org.qtproject.qt5.android.multimedia.QtMultimediaUtils"/>
<!-- Used to specify custom system library path to run with local system libs -->
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
</activity>
<activity android:name="org.qtproject.qt5.android.bindings.QtActivity" />
</application>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
package org.krita.android;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Copies the default configurations from assets to INTERNAL_STORAGE
* on first run of the app.
*/
class ConfigsManager {
private final String LOG_TAG = "krita.ConfigsManager";
private final String FIRST_RUN_COOKIE = "ORG_KRITA_FIRST_RUN";
private Activity mActivity;
private boolean isFirstRun() {
return mActivity.getPreferences(Context.MODE_PRIVATE)
.getBoolean(FIRST_RUN_COOKIE, true);
}
private void setFirstRunCookie() {
SharedPreferences sharedPref = mActivity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(FIRST_RUN_COOKIE, false);
editor.apply();
}
void handleAssets(Activity activity) {
mActivity = activity;
if (!isFirstRun()) {
return;
}
Log.d(LOG_TAG, mActivity.getFilesDir().getPath());
copyAssets();
setFirstRunCookie();
}
private void copyAssets() {
recurse("");
}
/**
* Recurse into directories
* Do not start path with `/` or end with `/`
* @param path relative path to asset
*/
private void recurse(String path) {
AssetManager assetManager = mActivity.getAssets();
String assets[];
try {
assets = assetManager.list(path);
if (assets == null) {
return;
}
// no assets inside, so a file
if (assets.length == 0) {
copyFile(path);
}
else {
for (String asset : assets) {
recurse(toPath(path, asset));
}
}
} catch (IOException ex) {
Log.e(LOG_TAG, "Could not access assets stream", ex);
}
}
/**
* Asset manager API doesn't like trailing `/` at the end and
* some other things so we need this for proper path
* @param args file names or directories
* @return sanitized path
*/
private String toPath(String... args) {
StringBuilder result = new StringBuilder();
for (String item: args) {
if (!item.isEmpty()) {
if (result.length() != 0 && result.charAt(result.length() - 1) != '/') {
result.append('/');
}
result.append(item);
if (item.charAt(item.length() - 1) != '/') {
result.append('/');
}
}
}
// if last character is '/', then delete it
if (result.charAt(result.length() - 1) == '/') {
result.deleteCharAt(result.length() - 1);
}
return result.toString();
}
private void copyFile(String name) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = mActivity.getAssets().open(name);
String fileSavePath = toPath(mActivity.getFilesDir().getPath(), "/share/", name);
// use the same directory structure
File base = new File(basePath(fileSavePath));
if (!base.exists()) {
base.mkdirs();
}
out = new FileOutputStream(fileSavePath);
byte[] buffer = new byte[4 * 1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
catch (IOException e) {
Log.w(LOG_TAG, "Could not copy file: " + name, e);
}
finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
/**
* Returns base path for example, in /data/foo it would
* return /data
* @param path to be sanitized
* @return base path
*/
private String basePath(String path) {
for (int i = path.length() - 1; i >= 0; --i) {
if (path.charAt(i) == '/') {
return path.substring(0, i);
}
}
return "";
}
}
package org.krita.android;
import android.os.Bundle;
import android.view.WindowManager;
import org.qtproject.qt5.android.bindings.QtActivity;
public class MainActivity extends QtActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
new ConfigsManager().handleAssets(this);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment