Merge branch 'version/0.5.1'
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "qzxing"]
|
||||
path = qzxing
|
||||
url = https://github.com/ftylitak/qzxing.git
|
23
CHANGELOG.md
|
@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# [0.6.1] - 2021-10-20
|
||||
### Added
|
||||
- Privacy policy link to comply with google play policies
|
||||
|
||||
# [0.6.0] - 2021-08-07
|
||||
### Changed
|
||||
- The subtitle in results and startlists is now the route name instead of the category name
|
||||
|
||||
### Added
|
||||
- Dark mode
|
||||
- QR-Code scanning
|
||||
- Sharing of every view using either link, QR-Code or a poster
|
||||
- Text which is too large too fit is scrollable now in most places
|
||||
- German translations
|
||||
- URL handler for https://l.bluerock.dev and https://app.bluerock.dev
|
||||
|
||||
### Fixed
|
||||
- Rare issue with missing background in boulder result rect
|
||||
|
||||
# [0.5.1] - 2021-07-06
|
||||
### Fixed
|
||||
- In-app purchase
|
||||
|
@ -20,7 +39,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- the calendar now scrolls less far down
|
||||
- improoved layout in landscape mode
|
||||
- some design changes in profile page and speed flowchart
|
||||
- added second link for "further infos" in calendar
|
||||
|
||||
### Added
|
||||
- Second link for "further infos" in calendar
|
||||
|
||||
# [0.03.0] - 2019-07-11
|
||||
### Added
|
||||
|
|
19
README.md
|
@ -1,3 +1,20 @@
|
|||
# Digital Rock ranking
|
||||
|
||||
App to view ranking and calendar data from https://www.digitalrock.de/
|
||||
App to view ranking and calendar data from https://www.digitalrock.de/
|
||||
|
||||
# Init
|
||||
´´´
|
||||
git submodule init
|
||||
git submodule update
|
||||
´´´
|
||||
|
||||
# Poster
|
||||
The poster offsets are (always top left of the element):
|
||||
- Width: 1654
|
||||
- Height: 2339
|
||||
### QR-Code
|
||||
- Cooridnates: 414, 414
|
||||
- Size: 1650x1650
|
||||
### Comp name
|
||||
- Cooridnates: x: 324, y: 2500
|
||||
- Size: 64 per line; 1835 width; 150 max height
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.blueROCK.MainActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
@ -68,9 +68,23 @@
|
|||
-->
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||
<!-- extract android style -->
|
||||
<!-- Handle shared incoming urls -->
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<!-- Accepts URIs that begin with "https://l.bluerock.dev/” -->
|
||||
<data android:scheme="https" android:host="l.bluerock.dev" android:pathPattern=".*"/>
|
||||
<!-- Accepts URIs that begin with "https://app.bluerock.dev” -->
|
||||
<data android:scheme="https" android:host="app.bluerock.dev" android:pathPattern=".*"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
<provider android:name="android.support.v4.content.FileProvider" android:authorities="de.itsblue.blueROCK.fileprovider" android:grantUriPermissions="true" android:exported="false">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -18,6 +18,7 @@ apply plugin: 'com.android.application'
|
|||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
compile 'com.android.support:support-v4:25.3.1'
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -72,10 +73,11 @@ android {
|
|||
defaultConfig {
|
||||
resConfig "en"
|
||||
minSdkVersion = qtMinSdkVersion
|
||||
targetSdkVersion = 29
|
||||
targetSdkVersion = qtTargetSdkVersion
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
|
3
android/res/xml/filepaths.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<files-path name="temporaryFiles" path="temporaryFiles/" />
|
||||
</paths>
|
232
android/src/de/itsblue/blueROCK/MainActivity.java
Executable file
|
@ -0,0 +1,232 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke)
|
||||
// this project is based on ideas from
|
||||
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||
// also inspired by:
|
||||
// https://www.androidcode.ninja/android-share-intent-example/
|
||||
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||
// OpenURL in At Android: got ideas from:
|
||||
// https://github.com/BernhardWenzel/open-url-in-qt-android
|
||||
// https://github.com/tobiatesan/android_intents_qt
|
||||
//
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
package de.itsblue.blueROCK;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
import android.os.*;
|
||||
import android.content.*;
|
||||
import android.app.*;
|
||||
|
||||
import java.lang.String;
|
||||
import android.content.Intent;
|
||||
import java.io.File;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.content.ContentResolver;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.ekkescorner.utils.*;
|
||||
|
||||
|
||||
|
||||
public class MainActivity extends QtActivity
|
||||
{
|
||||
// native - must be implemented in Cpp via JNI
|
||||
// 'file' scheme or resolved from 'content' scheme:
|
||||
public static native void setFileUrlReceived(String url);
|
||||
//
|
||||
public static native void setOtherUrlReceived(String url, String scheme);
|
||||
// InputStream from 'content' scheme:
|
||||
public static native void setFileReceivedAndSaved(String url);
|
||||
//
|
||||
public static native void fireActivityResult(int requestCode, int resultCode);
|
||||
//
|
||||
public static native boolean checkFileExits(String url);
|
||||
|
||||
public static boolean isIntentPending;
|
||||
public static boolean isInitialized;
|
||||
public static String workingDirPath;
|
||||
|
||||
// Use a custom Chooser without providing own App as share target !
|
||||
// see QShareUtils.java createCustomChooserAndStartActivity()
|
||||
// Selecting your own App as target could cause AndroidOS to call
|
||||
// onCreate() instead of onNewIntent()
|
||||
// and then you are in trouble because we're using 'singleInstance' as LaunchMode
|
||||
// more details: my blog at Qt
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.d("ekkescorner", "onCreate QShareActivity");
|
||||
// now we're checking if the App was started from another Android App via Intent
|
||||
Intent theIntent = getIntent();
|
||||
if (theIntent != null){
|
||||
String theAction = theIntent.getAction();
|
||||
if (theAction != null){
|
||||
Log.d("ekkescorner onCreate ", theAction);
|
||||
// QML UI not ready yet
|
||||
// delay processIntent();
|
||||
isIntentPending = true;
|
||||
}
|
||||
}
|
||||
} // onCreate
|
||||
|
||||
// WIP - trying to find a solution to survive a 2nd onCreate
|
||||
// ongoing discussion in QtMob (Slack)
|
||||
// from other Apps not respecting that you only have a singleInstance
|
||||
// there are problems per ex. sharing a file from Google Files App,
|
||||
// but working well using Xiaomi FileManager App
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d("ekkescorner", "onDestroy QShareActivity");
|
||||
// super.onDestroy();
|
||||
// System.exit() closes the App before doing onCreate() again
|
||||
// then the App was restarted, but looses context
|
||||
// This works for Samsung My Files
|
||||
// but Google Files doesn't call onDestroy()
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// we start Activity with result code
|
||||
// to test JNI with QAndroidActivityResultReceiver you must comment or rename
|
||||
// this method here - otherwise you'll get wrong request or result codes
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// Check which request we're responding to
|
||||
Log.d("ekkescorner onActivityResult", "requestCode: "+requestCode);
|
||||
if (resultCode == RESULT_OK) {
|
||||
Log.d("ekkescorner onActivityResult - resultCode: ", "SUCCESS");
|
||||
} else {
|
||||
Log.d("ekkescorner onActivityResult - resultCode: ", "CANCEL");
|
||||
}
|
||||
// hint: result comes back too fast for Action SEND
|
||||
// if you want to delete/move the File add a Timer w 500ms delay
|
||||
// see Example App main.qml - delayDeleteTimer
|
||||
// if you want to revoke permissions for older OS
|
||||
// it makes sense also do this after the delay
|
||||
fireActivityResult(requestCode, resultCode);
|
||||
}
|
||||
|
||||
// if we are opened from other apps:
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
Log.d("ekkescorner", "onNewIntent");
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
// Intent will be processed, if all is initialized and Qt / QML can handle the event
|
||||
if(isInitialized) {
|
||||
processIntent();
|
||||
} else {
|
||||
isIntentPending = true;
|
||||
}
|
||||
} // onNewIntent
|
||||
|
||||
public void checkPendingIntents(String workingDir) {
|
||||
isInitialized = true;
|
||||
workingDirPath = workingDir;
|
||||
Log.d("ekkescorner", workingDirPath);
|
||||
if(isIntentPending) {
|
||||
isIntentPending = false;
|
||||
Log.d("ekkescorner", "checkPendingIntents: true");
|
||||
processIntent();
|
||||
} else {
|
||||
Log.d("ekkescorner", "nothingPending");
|
||||
}
|
||||
} // checkPendingIntents
|
||||
|
||||
// process the Intent if Action is SEND or VIEW
|
||||
private void processIntent(){
|
||||
Intent intent = getIntent();
|
||||
|
||||
Uri intentUri;
|
||||
String intentScheme;
|
||||
String intentAction;
|
||||
// we are listening to android.intent.action.SEND or VIEW (see Manifest)
|
||||
if (intent.getAction().equals("android.intent.action.VIEW")){
|
||||
intentAction = "VIEW";
|
||||
intentUri = intent.getData();
|
||||
} else if (intent.getAction().equals("android.intent.action.SEND")){
|
||||
intentAction = "SEND";
|
||||
Bundle bundle = intent.getExtras();
|
||||
intentUri = (Uri)bundle.get(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Log.d("ekkescorner Intent unknown action:", intent.getAction());
|
||||
return;
|
||||
}
|
||||
Log.d("ekkescorner action:", intentAction);
|
||||
if (intentUri == null){
|
||||
Log.d("ekkescorner Intent URI:", "is null");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d("ekkescorner Intent URI:", intentUri.toString());
|
||||
|
||||
// content or file
|
||||
intentScheme = intentUri.getScheme();
|
||||
if (intentScheme == null){
|
||||
Log.d("ekkescorner Intent URI Scheme:", "is null");
|
||||
return;
|
||||
}
|
||||
if(intentScheme.equals("file")){
|
||||
// URI as encoded string
|
||||
Log.d("ekkescorner Intent File URI: ", intentUri.toString());
|
||||
setFileUrlReceived(intentUri.toString());
|
||||
// we are done Qt can deal with file scheme
|
||||
return;
|
||||
}
|
||||
if(!intentScheme.equals("content")){
|
||||
Log.d("ekkescorner Intent URI unknown scheme: ", intentScheme);
|
||||
setOtherUrlReceived(intentUri.toString(), intentScheme);
|
||||
return;
|
||||
}
|
||||
// ok - it's a content scheme URI
|
||||
// we will try to resolve the Path to a File URI
|
||||
// if this won't work or if the File cannot be opened,
|
||||
// we'll try to copy the file into our App working dir via InputStream
|
||||
// hopefully in most cases PathResolver will give a path
|
||||
|
||||
// you need the file extension, MimeType or Name from ContentResolver ?
|
||||
// here's HowTo get it:
|
||||
Log.d("ekkescorner Intent Content URI: ", intentUri.toString());
|
||||
ContentResolver cR = this.getContentResolver();
|
||||
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
||||
String fileExtension = mime.getExtensionFromMimeType(cR.getType(intentUri));
|
||||
Log.d("ekkescorner","Intent extension: "+fileExtension);
|
||||
String mimeType = cR.getType(intentUri);
|
||||
Log.d("ekkescorner"," Intent MimeType: "+mimeType);
|
||||
String name = QShareUtils.getContentName(cR, intentUri);
|
||||
if(name != null) {
|
||||
Log.d("ekkescorner Intent Name:", name);
|
||||
} else {
|
||||
Log.d("ekkescorner Intent Name:", "is NULL");
|
||||
}
|
||||
String filePath;
|
||||
filePath = QSharePathResolver.getRealPathFromURI(this, intentUri);
|
||||
if(filePath == null) {
|
||||
Log.d("ekkescorner QSharePathResolver:", "filePath is NULL");
|
||||
} else {
|
||||
Log.d("ekkescorner QSharePathResolver:", filePath);
|
||||
// to be safe check if this File Url really can be opened by Qt
|
||||
// there were problems with MS office apps on Android 7
|
||||
if (checkFileExits(filePath)) {
|
||||
setFileUrlReceived(filePath);
|
||||
// we are done Qt can deal with file scheme
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// trying the InputStream way:
|
||||
filePath = QShareUtils.createFile(cR, intentUri, workingDirPath);
|
||||
if(filePath == null) {
|
||||
Log.d("ekkescorner Intent FilePath:", "is NULL");
|
||||
return;
|
||||
}
|
||||
setFileReceivedAndSaved(filePath);
|
||||
} // processIntent
|
||||
|
||||
} // class QShareActivity
|
223
android/src/org/ekkescorner/utils/QSharePathResolver.java
Normal file
|
@ -0,0 +1,223 @@
|
|||
// from: https://github.com/wkh237/react-native-fetch-blob/blob/master/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java
|
||||
// MIT License, see: https://github.com/wkh237/react-native-fetch-blob/blob/master/LICENSE
|
||||
// original copyright: Copyright (c) 2017 xeiyan@gmail.com
|
||||
// src slightly modified to be used into Qt Projects: (c) 2017 ekke@ekkes-corner.org
|
||||
|
||||
package org.ekkescorner.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.content.ContentUris;
|
||||
import android.os.Environment;
|
||||
import android.content.ContentResolver;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import android.util.Log;
|
||||
import java.lang.NumberFormatException;
|
||||
|
||||
public class QSharePathResolver {
|
||||
public static String getRealPathFromURI(final Context context, final Uri uri) {
|
||||
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
Log.d("ekkescorner"," isExternalStorageDocument");
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
Log.d("ekkescorner"," isDownloadsDocument");
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
Log.d("ekkescorner"," getDocumentId "+id);
|
||||
long longId = 0;
|
||||
try
|
||||
{
|
||||
longId = Long.valueOf(id);
|
||||
}
|
||||
catch(NumberFormatException nfe)
|
||||
{
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), longId);
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
Log.d("ekkescorner"," isMediaDocument");
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[] {
|
||||
split[1]
|
||||
};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
Log.d("ekkescorner"," is uri.getScheme()");
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// Other Providers
|
||||
else{
|
||||
Log.d("ekkescorner ","is Other Provider");
|
||||
try {
|
||||
InputStream attachment = context.getContentResolver().openInputStream(uri);
|
||||
if (attachment != null) {
|
||||
String filename = getContentName(context.getContentResolver(), uri);
|
||||
if (filename != null) {
|
||||
File file = new File(context.getCacheDir(), filename);
|
||||
FileOutputStream tmp = new FileOutputStream(file);
|
||||
byte[] buffer = new byte[1024];
|
||||
while (attachment.read(buffer) > 0) {
|
||||
tmp.write(buffer);
|
||||
}
|
||||
tmp.close();
|
||||
attachment.close();
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO SIGNAL shareError()
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
Log.d("ekkescorner ","NOT DocumentsContract.isDocumentUri");
|
||||
Log.d("ekkescorner"," is uri.getScheme()");
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
Log.d("ekkescorner"," return: getDataColumn ");
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
Log.d("ekkescorner ","NOT DocumentsContract.isDocumentUri");
|
||||
Log.d("ekkescorner"," is file scheme");
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getContentName(ContentResolver resolver, Uri uri) {
|
||||
Cursor cursor = resolver.query(uri, null, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
if (nameIndex >= 0) {
|
||||
String name = cursor.getString(nameIndex);
|
||||
cursor.close();
|
||||
return name;
|
||||
}
|
||||
cursor.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||
String[] selectionArgs) {
|
||||
|
||||
Cursor cursor = null;
|
||||
String result = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {
|
||||
column
|
||||
};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
result = cursor.getString(index);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
public static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
public static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
public static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
public static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
}
|
396
android/src/org/ekkescorner/utils/QShareUtils.java
Executable file
|
@ -0,0 +1,396 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke)
|
||||
// this project is based on ideas from
|
||||
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||
// also inspired by:
|
||||
// https://www.androidcode.ninja/android-share-intent-example/
|
||||
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||
// https://stackoverflow.com/questions/5734678/custom-filtering-of-intent-chooser-based-on-installed-android-package-name
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
package org.ekkescorner.utils;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
import java.lang.String;
|
||||
import android.content.Intent;
|
||||
import java.io.File;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.util.List;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import java.util.ArrayList;
|
||||
import android.content.pm.PackageManager;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.support.v4.app.ShareCompat;
|
||||
|
||||
public class QShareUtils
|
||||
{
|
||||
// reference Authority as defined in AndroidManifest.xml
|
||||
private static String AUTHORITY="de.itsblue.blueROCK.fileprovider";
|
||||
|
||||
protected QShareUtils()
|
||||
{
|
||||
Log.d("ekkescorner", "QShareUtils()");
|
||||
}
|
||||
|
||||
public static boolean checkMimeTypeView(String mimeType) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
Intent myIntent = new Intent();
|
||||
myIntent.setAction(Intent.ACTION_VIEW);
|
||||
// without an URI resolve always fails
|
||||
// an empty URI allows to resolve the Activity
|
||||
File fileToShare = new File("");
|
||||
Uri uri = Uri.fromFile(fileToShare);
|
||||
myIntent.setDataAndType(uri, mimeType);
|
||||
|
||||
// Verify that the intent will resolve to an activity
|
||||
if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||
Log.d("ekkescorner checkMime ", "YEP - we can go on and View");
|
||||
return true;
|
||||
} else {
|
||||
Log.d("ekkescorner checkMime", "sorry - no App available to View");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean checkMimeTypeEdit(String mimeType) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
Intent myIntent = new Intent();
|
||||
myIntent.setAction(Intent.ACTION_EDIT);
|
||||
// without an URI resolve always fails
|
||||
// an empty URI allows to resolve the Activity
|
||||
File fileToShare = new File("");
|
||||
Uri uri = Uri.fromFile(fileToShare);
|
||||
myIntent.setDataAndType(uri, mimeType);
|
||||
|
||||
// Verify that the intent will resolve to an activity
|
||||
if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||
Log.d("ekkescorner checkMime ", "YEP - we can go on and Edit");
|
||||
return true;
|
||||
} else {
|
||||
Log.d("ekkescorner checkMime", "sorry - no App available to Edit");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean shareText(String text) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
sendIntent.setType("text/plain");
|
||||
|
||||
// Verify that the intent will resolve to an activity
|
||||
if (sendIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||
QtNative.activity().startActivity(sendIntent);
|
||||
return true;
|
||||
} else {
|
||||
Log.d("ekkescorner share", "Intent not resolved");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// thx @oxied and @pooks for the idea: https://stackoverflow.com/a/18835895/135559
|
||||
// theIntent is already configured with all needed properties and flags
|
||||
// so we only have to add the packageName of targeted app
|
||||
public static boolean createCustomChooserAndStartActivity(Intent theIntent, String title, int requestId, Uri uri) {
|
||||
final Context context = QtNative.activity();
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
final boolean isLowerOrEqualsKitKat = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
// MATCH_DEFAULT_ONLY: Resolution and querying flag. if set, only filters that support the CATEGORY_DEFAULT will be considered for matching.
|
||||
// Check if there is a default app for this type of content.
|
||||
ResolveInfo defaultAppInfo = packageManager.resolveActivity(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if(defaultAppInfo == null) {
|
||||
Log.d("ekkescorner", title+" PackageManager cannot resolve Activity");
|
||||
return false;
|
||||
}
|
||||
|
||||
// had to remove this check - there can be more Activity names, per ex
|
||||
// com.google.android.apps.docs.editors.kix.quickword.QuickWordDocumentOpenerActivityAlias
|
||||
// if (!defaultAppInfo.activityInfo.name.endsWith("ResolverActivity") && !defaultAppInfo.activityInfo.name.endsWith("EditActivity")) {
|
||||
// Log.d("ekkescorner", title+" defaultAppInfo not Resolver or EditActivity: "+defaultAppInfo.activityInfo.name);
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// Retrieve all apps for our intent. Check if there are any apps returned
|
||||
List<ResolveInfo> appInfoList = packageManager.queryIntentActivities(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (appInfoList.isEmpty()) {
|
||||
Log.d("ekkescorner", title+" appInfoList.isEmpty");
|
||||
return false;
|
||||
}
|
||||
Log.d("ekkescorner", title+" appInfoList: "+appInfoList.size());
|
||||
|
||||
// Sort in alphabetical order
|
||||
Collections.sort(appInfoList, new Comparator<ResolveInfo>() {
|
||||
@Override
|
||||
public int compare(ResolveInfo first, ResolveInfo second) {
|
||||
String firstName = first.loadLabel(packageManager).toString();
|
||||
String secondName = second.loadLabel(packageManager).toString();
|
||||
return firstName.compareToIgnoreCase(secondName);
|
||||
}
|
||||
});
|
||||
|
||||
List<Intent> targetedIntents = new ArrayList<Intent>();
|
||||
// Filter itself and create intent with the rest of the apps.
|
||||
for (ResolveInfo appInfo : appInfoList) {
|
||||
// get the target PackageName
|
||||
String targetPackageName = appInfo.activityInfo.packageName;
|
||||
// we don't want to share with our own app
|
||||
// in fact sharing with own app with resultCode will crash because doesn't work well with launch mode 'singleInstance'
|
||||
if (targetPackageName.equals(context.getPackageName())) {
|
||||
continue;
|
||||
}
|
||||
// if you have a blacklist of apps please exclude them here
|
||||
|
||||
// we create the targeted Intent based on our already configured Intent
|
||||
Intent targetedIntent = new Intent(theIntent);
|
||||
// now add the target packageName so this Intent will only find the one specific App
|
||||
targetedIntent.setPackage(targetPackageName);
|
||||
// collect all these targetedIntents
|
||||
targetedIntents.add(targetedIntent);
|
||||
|
||||
// legacy support and Workaround for Android bug
|
||||
// grantUriPermission needed for KITKAT or older
|
||||
// see https://code.google.com/p/android/issues/detail?id=76683
|
||||
// also: https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
|
||||
if(isLowerOrEqualsKitKat) {
|
||||
Log.d("ekkescorner", "legacy support grantUriPermission");
|
||||
context.grantUriPermission(targetPackageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
// attention: you must revoke the permission later, so this only makes sense with getting back a result to know that Intent was done
|
||||
// I always move or delete the file, so I don't revoke permission
|
||||
}
|
||||
}
|
||||
|
||||
// check if there are apps found for our Intent to avoid that there was only our own removed app before
|
||||
if (targetedIntents.isEmpty()) {
|
||||
Log.d("ekkescorner", title+" targetedIntents.isEmpty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// now we can create our Intent with custom Chooser
|
||||
// we need all collected targetedIntents as EXTRA_INITIAL_INTENTS
|
||||
// we're using the last targetedIntent as initializing Intent, because
|
||||
// chooser adds its initializing intent to the end of EXTRA_INITIAL_INTENTS :)
|
||||
Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() - 1), title);
|
||||
if (targetedIntents.isEmpty()) {
|
||||
Log.d("ekkescorner", title+" only one Intent left for Chooser");
|
||||
} else {
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[] {}));
|
||||
}
|
||||
// Verify that the intent will resolve to an activity
|
||||
if (chooserIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||
if(requestId > 0) {
|
||||
QtNative.activity().startActivityForResult(chooserIntent, requestId);
|
||||
} else {
|
||||
QtNative.activity().startActivity(chooserIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Log.d("ekkescorner", title+" Chooser Intent not resolved. Should never happen");
|
||||
return false;
|
||||
}
|
||||
|
||||
// I am deleting the files from shared folder when Activity was done or canceled
|
||||
// so probably I don't have to revike FilePermissions for older OS
|
||||
// if you don't delete or move the file: here's what you must done to revoke the access
|
||||
public static void revokeFilePermissions(String filePath) {
|
||||
final Context context = QtNative.activity();
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
||||
File file = new File(filePath);
|
||||
Uri uri = FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||
context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean sendFile(String filePath, String title, String mimeType, int requestId) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
|
||||
// using v4 support library create the Intent from ShareCompat
|
||||
// Intent sendIntent = new Intent();
|
||||
Intent sendIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
|
||||
File imageFileToShare = new File(filePath);
|
||||
|
||||
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||
Uri uri;
|
||||
try {
|
||||
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d("ekkescorner sendFile - cannot be shared: ", filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d("ekkescorner sendFile", uri.toString());
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
|
||||
if(mimeType == null || mimeType.isEmpty()) {
|
||||
// fallback if mimeType not set
|
||||
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||
Log.d("ekkescorner sendFile guessed mimeType:", mimeType);
|
||||
} else {
|
||||
Log.d("ekkescorner sendFile w mimeType:", mimeType);
|
||||
}
|
||||
|
||||
sendIntent.setType(mimeType);
|
||||
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
return createCustomChooserAndStartActivity(sendIntent, title, requestId, uri);
|
||||
}
|
||||
|
||||
public static boolean viewFile(String filePath, String title, String mimeType, int requestId) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
|
||||
// using v4 support library create the Intent from ShareCompat
|
||||
// Intent viewIntent = new Intent();
|
||||
Intent viewIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||
viewIntent.setAction(Intent.ACTION_VIEW);
|
||||
|
||||
File imageFileToShare = new File(filePath);
|
||||
|
||||
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||
Uri uri;
|
||||
try {
|
||||
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d("ekkescorner viewFile - cannot be shared: ", filePath);
|
||||
return false;
|
||||
}
|
||||
// now we got a content URI per ex
|
||||
// content://org.ekkescorner.examples.sharex.fileprovider/my_shared_files/qt-logo.png
|
||||
// from a fileUrl:
|
||||
// /data/user/0/org.ekkescorner.examples.sharex/files/share_example_x_files/qt-logo.png
|
||||
Log.d("ekkescorner viewFile from file path: ", filePath);
|
||||
Log.d("ekkescorner viewFile to content URI: ", uri.toString());
|
||||
|
||||
if(mimeType == null || mimeType.isEmpty()) {
|
||||
// fallback if mimeType not set
|
||||
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||
Log.d("ekkescorner viewFile guessed mimeType:", mimeType);
|
||||
} else {
|
||||
Log.d("ekkescorner viewFile w mimeType:", mimeType);
|
||||
}
|
||||
|
||||
viewIntent.setDataAndType(uri, mimeType);
|
||||
|
||||
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
viewIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
return createCustomChooserAndStartActivity(viewIntent, title, requestId, uri);
|
||||
}
|
||||
|
||||
public static boolean editFile(String filePath, String title, String mimeType, int requestId) {
|
||||
if (QtNative.activity() == null)
|
||||
return false;
|
||||
|
||||
// using v4 support library create the Intent from ShareCompat
|
||||
// Intent editIntent = new Intent();
|
||||
Intent editIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||
editIntent.setAction(Intent.ACTION_EDIT);
|
||||
|
||||
File imageFileToShare = new File(filePath);
|
||||
|
||||
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||
Uri uri;
|
||||
try {
|
||||
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d("ekkescorner editFile - cannot be shared: ", filePath);
|
||||
return false;
|
||||
}
|
||||
Log.d("ekkescorner editFile", uri.toString());
|
||||
|
||||
if(mimeType == null || mimeType.isEmpty()) {
|
||||
// fallback if mimeType not set
|
||||
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||
Log.d("ekkescorner editFile guessed mimeType:", mimeType);
|
||||
} else {
|
||||
Log.d("ekkescorner editFile w mimeType:", mimeType);
|
||||
}
|
||||
|
||||
editIntent.setDataAndType(uri, mimeType);
|
||||
|
||||
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
return createCustomChooserAndStartActivity(editIntent, title, requestId, uri);
|
||||
}
|
||||
|
||||
public static String getContentName(ContentResolver cR, Uri uri) {
|
||||
Cursor cursor = cR.query(uri, null, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
int nameIndex = cursor
|
||||
.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
if (nameIndex >= 0) {
|
||||
return cursor.getString(nameIndex);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String createFile(ContentResolver cR, Uri uri, String fileLocation) {
|
||||
String filePath = null;
|
||||
try {
|
||||
InputStream iStream = cR.openInputStream(uri);
|
||||
if (iStream != null) {
|
||||
String name = getContentName(cR, uri);
|
||||
if (name != null) {
|
||||
filePath = fileLocation + "/" + name;
|
||||
Log.d("ekkescorner - create File", filePath);
|
||||
File f = new File(filePath);
|
||||
FileOutputStream tmp = new FileOutputStream(f);
|
||||
Log.d("ekkescorner - create File", "new FileOutputStream");
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
while (iStream.read(buffer) > 0) {
|
||||
tmp.write(buffer);
|
||||
}
|
||||
tmp.close();
|
||||
iStream.close();
|
||||
return filePath;
|
||||
} // name
|
||||
} // iStream
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return filePath;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return filePath;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return filePath;
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
}
|
106
blueROCK.pro
|
@ -1,7 +1,7 @@
|
|||
QT += quick qml quickcontrols2 purchasing widgets
|
||||
QT += quick qml quickcontrols2 purchasing
|
||||
CONFIG += c++11
|
||||
|
||||
VERSION = 0.5.1
|
||||
VERSION = 0.6.1
|
||||
TARGET = blueROCK
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
|
@ -15,16 +15,30 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
INCLUDEPATH += $$PWD/headers
|
||||
|
||||
# Add version to define
|
||||
DEFINES += APP_VERSION=\"\\\"$${VERSION}\\\"\"
|
||||
|
||||
SOURCES += \
|
||||
sources/shareUtils/platformshareutils.cpp \
|
||||
sources/appsettings.cpp \
|
||||
sources/main.cpp \
|
||||
sources/serverconn.cpp
|
||||
sources/bluerockbackend.cpp \
|
||||
sources/shareUtils/shareutils.cpp \
|
||||
sources/main.cpp
|
||||
|
||||
HEADERS += \
|
||||
headers/appsettings.h \
|
||||
headers/bluerockbackend.h \
|
||||
headers/shareUtils/shareutils.h \
|
||||
headers/shareUtils/platformshareutils.h
|
||||
|
||||
RESOURCES += resources/qml/qml.qrc \
|
||||
resources/shared/shared.qrc \
|
||||
#resources/shared/icons/bluerock/index.theme \
|
||||
#$$files(resources/shared/icons/*.png, true)
|
||||
resources/translations/translations.qrc
|
||||
|
||||
TRANSLATIONS += resources/translations/en.ts \
|
||||
resources/translations/de.ts
|
||||
|
||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
QML_IMPORT_PATH =
|
||||
|
@ -37,27 +51,29 @@ qnx: target.path = /tmp/$${TARGET}/bin
|
|||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
# Add version to define
|
||||
DEFINES += APP_VERSION=\"\\\"$${VERSION}\\\"\"
|
||||
|
||||
HEADERS += \
|
||||
headers/appsettings.h \
|
||||
headers/serverconn.h
|
||||
|
||||
DISTFILES += \
|
||||
CHANGELOG.md \
|
||||
android/AndroidManifest.xml \
|
||||
android/build.gradle \
|
||||
android/gradle.properties \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew \
|
||||
android/gradlew.bat \
|
||||
android/res/values/libs.xml
|
||||
README.md
|
||||
|
||||
android {
|
||||
QT += androidextras
|
||||
|
||||
SOURCES += sources/shareUtils/androidshareutils.cpp
|
||||
HEADERS += headers/shareUtils/androidshareutils.h
|
||||
|
||||
OTHER_FILES += android/src/org/ekkescorner/utils/QShareUtils.java \
|
||||
android/src/org/ekkescorner/utils/QSharePathResolver.java \
|
||||
android/src/de/itsblue/blueROCK/MainActivity.java \
|
||||
android/AndroidManifest.xml \
|
||||
android/build.gradle \
|
||||
android/gradle.properties \
|
||||
android/gradle/wrapper/gradle-wrapper.jar \
|
||||
android/gradle/wrapper/gradle-wrapper.properties \
|
||||
android/gradlew \
|
||||
android/gradlew.bat \
|
||||
android/res/values/libs.xml \
|
||||
android/res/xml/filepaths.xml
|
||||
|
||||
defineReplace(droidVersionCode) {
|
||||
segments = $$split(1, ".")
|
||||
for (segment, segments): vCode = "$$first(vCode)$$format_number($$segment, width=3 zeropad)"
|
||||
|
@ -73,17 +89,63 @@ android {
|
|||
|
||||
ANDROID_VERSION_NAME = $$VERSION
|
||||
ANDROID_VERSION_CODE = $$droidVersionCode($$ANDROID_VERSION_NAME)
|
||||
message(Android version code: $$ANDROID_VERSION_CODE)
|
||||
ANDROID_TARGET_SDK_VERSION = 29
|
||||
|
||||
include(/home/dorian/Android/Sdk/android_openssl/openssl.pri)
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
}
|
||||
|
||||
ios {
|
||||
OBJECTIVE_SOURCES += sources/shareUtils/ios/iosshareutils.mm \
|
||||
sources/iospermissionutils.mm \
|
||||
sources/shareUtils/ios/docviewcontroller.mm
|
||||
|
||||
HEADERS += headers/shareUtils/ios/iosshareutils.h \
|
||||
headers/iospermissionutils.h \
|
||||
headers/shareUtils/ios/docviewcontroller.h
|
||||
|
||||
OTHER_FILES += ios/Info.plist \
|
||||
ios/blueROCK.entitlements
|
||||
|
||||
QMAKE_INFO_PLIST = ios/Info.plist
|
||||
|
||||
|
||||
#QMAKE_IOS_DEPLOYMENT_TARGET = 12.0
|
||||
|
||||
#disable_warning.name = GCC_WARN_64_TO_32_BIT_CONVERSION
|
||||
#disable_warning.value = NO
|
||||
#QMAKE_MAC_XCODE_SETTINGS += disable_warning
|
||||
|
||||
# see https://bugreports.qt.io/browse/QTCREATORBUG-16968
|
||||
# ios_signature.pri not part of project repo because of private signature details
|
||||
# contains:
|
||||
# QMAKE_XCODE_CODE_SIGN_IDENTITY = "iPhone Developer"
|
||||
# MY_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
|
||||
# MY_DEVELOPMENT_TEAM.value = your team Id from Apple Developer Account
|
||||
# QMAKE_MAC_XCODE_SETTINGS += MY_DEVELOPMENT_TEAM
|
||||
|
||||
#include(ios_signature.pri)
|
||||
|
||||
QMAKE_ASSET_CATALOGS += resources/shared/Assets.xcassets
|
||||
|
||||
MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
|
||||
MY_ENTITLEMENTS.value = $$PWD/ios/blueROCK.entitlements
|
||||
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS
|
||||
|
||||
MY_BUNDLE_ID.name = PRODUCT_BUNDLE_IDENTIFIER
|
||||
MY_BUNDLE_ID.value = de.itsblue.bluerock
|
||||
QMAKE_MAC_XCODE_SETTINGS += MY_BUNDLE_ID
|
||||
xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock"
|
||||
PRODUCT_IDENTIFIER = de.itsblue.bluerock
|
||||
}
|
||||
|
||||
CONFIG += enable_decoder_qr_code \
|
||||
enable_encoder_qr_code \
|
||||
qzxing_multimedia \
|
||||
qzxing_qml
|
||||
|
||||
include(qzxing/src/QZXing-components.pri)
|
||||
|
||||
# this has to be the last line!
|
||||
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
||||
|
||||
|
|
|
@ -21,12 +21,6 @@ private:
|
|||
QSettings *settingsManager;
|
||||
// QSettings object which cares about our settings.ini file
|
||||
|
||||
QSettings *themeSettingsManager;
|
||||
// QSettings object which cares about the themes
|
||||
|
||||
signals:
|
||||
void themeChanged();
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QString read(const QString &key);
|
||||
// function to read values from the settings file
|
||||
|
|
81
headers/bluerockbackend.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
blueROCK - for digital rock
|
||||
Copyright (C) 2019 Dorian Zedler
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERVERCONN_H
|
||||
#define SERVERCONN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QBrush>
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QPageSize>
|
||||
#include <QPdfWriter>
|
||||
#include <QSizeF>
|
||||
#include "QZXing.h"
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QtAndroidExtras>
|
||||
#elif defined Q_OS_IOS
|
||||
#include "iospermissionutils.h"
|
||||
#endif
|
||||
|
||||
#include "shareUtils/shareutils.h"
|
||||
|
||||
class BlueRockBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BlueRockBackend(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
QVariantMap _senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
||||
|
||||
ShareUtils* _shareUtils;
|
||||
#ifdef Q_OS_IOS
|
||||
IosPermissionUtils* _iosPermissionUtils;
|
||||
#endif
|
||||
const QStringList _validBaseDomains = {"digitalrock.de", "bluerock.dev"};
|
||||
bool _pendingIntentsChecked;
|
||||
|
||||
signals:
|
||||
Q_INVOKABLE void openedViaUrl(QString url, QString scheme);
|
||||
|
||||
public slots:
|
||||
|
||||
Q_INVOKABLE QVariant getWidgetData(QVariantMap params);
|
||||
Q_INVOKABLE QVariantMap getParamsFromUrl(QString url);
|
||||
Q_INVOKABLE void shareResultsAsUrl(QString url, QString compName);
|
||||
Q_INVOKABLE void shareResultsAsPoster(QString url, QString compName);
|
||||
|
||||
Q_INVOKABLE bool isCameraPermissionGranted();
|
||||
Q_INVOKABLE bool requestCameraPermission();
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void onApplicationStateChanged(Qt::ApplicationState applicationState);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // SERVERCONN_H
|
22
headers/iospermissionutils.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef IOSPERMISSIONUTILS_H
|
||||
#define IOSPERMISSIONUTILS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
|
||||
class IosPermissionUtils : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IosPermissionUtils();
|
||||
bool isCameraPermissionGranted();
|
||||
bool requestCameraPermission();
|
||||
|
||||
private:
|
||||
QEventLoop* _responseWaitLoop;
|
||||
|
||||
signals:
|
||||
void permissionRequestFinished(bool result);
|
||||
};
|
||||
|
||||
#endif // IOSPERMISSIONUTILS_H
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
blueROCK - for digital rock
|
||||
Copyright (C) 2019 Dorian Zedler
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERVERCONN_H
|
||||
#define SERVERCONN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
class ServerConn : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerConn(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
QVariantMap senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
QVariant getWidgetData(QVariantMap params);
|
||||
|
||||
};
|
||||
|
||||
#endif // SERVERCONN_H
|
51
headers/shareUtils/androidshareutils.h
Executable file
|
@ -0,0 +1,51 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
#ifndef ANDROIDSHAREUTILS_H
|
||||
#define ANDROIDSHAREUTILS_H
|
||||
|
||||
#include <QtAndroid>
|
||||
#include <QAndroidActivityResultReceiver>
|
||||
|
||||
#include "shareUtils/platformshareutils.h"
|
||||
|
||||
class AndroidShareUtils : public PlatformShareUtils, public QAndroidActivityResultReceiver
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AndroidShareUtils(QObject* parent = nullptr);
|
||||
bool checkMimeTypeView(const QString &mimeType) override;
|
||||
bool checkMimeTypeEdit(const QString &mimeType) override;
|
||||
virtual QString getTemporaryFileLocationPath() override;
|
||||
void shareText(const QString &text, const QUrl &url) override;
|
||||
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
|
||||
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
|
||||
void onActivityResult(int requestCode, int resultCode);
|
||||
|
||||
void checkPendingIntents(const QString workingDirPath) override;
|
||||
|
||||
static AndroidShareUtils* getInstance();
|
||||
|
||||
public slots:
|
||||
void setFileUrlReceived(const QString &url);
|
||||
void setOtherUrlReceived(const QString &url, const QString &scheme);
|
||||
void setFileReceivedAndSaved(const QString &url);
|
||||
bool checkFileExits(const QString &url);
|
||||
|
||||
private:
|
||||
bool mIsEditMode;
|
||||
qint64 mLastModified;
|
||||
QString mCurrentFilePath;
|
||||
|
||||
static AndroidShareUtils* mInstance;
|
||||
|
||||
void processActivityResult(int requestCode, int resultCode);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // ANDROIDSHAREUTILS_H
|
21
headers/shareUtils/ios/docviewcontroller.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
#ifndef DOCVIEWCONTROLLER_HPP
|
||||
#define DOCVIEWCONTROLLER_HPP
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "iosshareutils.h"
|
||||
|
||||
@interface DocViewController : UIViewController <UIDocumentInteractionControllerDelegate>
|
||||
|
||||
@property int requestId;
|
||||
|
||||
@property IosShareUtils *mIosShareUtils;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#endif // DOCVIEWCONTROLLER_HPP
|
31
headers/shareUtils/ios/iosshareutils.h
Executable file
|
@ -0,0 +1,31 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
#ifndef __IOSSHAREUTILS_H__
|
||||
#define __IOSSHAREUTILS_H__
|
||||
|
||||
#include "headers/shareUtils/platformshareutils.h"
|
||||
|
||||
class IosShareUtils : public PlatformShareUtils
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IosShareUtils(QObject *parent = 0);
|
||||
bool checkMimeTypeView(const QString &mimeType) override;
|
||||
bool checkMimeTypeEdit(const QString &mimeType) override;
|
||||
void shareText(const QString &text, const QUrl &url) override;
|
||||
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||
|
||||
void handleDocumentPreviewDone(const int &requestId);
|
||||
|
||||
public slots:
|
||||
void handleFileUrlReceived(const QUrl &url);
|
||||
void handleHttpsUrlReceived(const QUrl &url);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
51
headers/shareUtils/platformshareutils.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke)
|
||||
// this project is based on ideas from
|
||||
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||
// also inspired by:
|
||||
// https://www.androidcode.ninja/android-share-intent-example/
|
||||
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
#ifndef PLATFORMSHAREUTILS_H
|
||||
#define PLATFORMSHAREUTILS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
class PlatformShareUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void shareEditDone(int requestCode);
|
||||
void shareFinished(int requestCode);
|
||||
void shareNoAppAvailable(int requestCode);
|
||||
void shareError(int requestCode, QString message);
|
||||
void fileUrlReceived(QString url);
|
||||
void otherUrlReceived(QString url, QString scheme);
|
||||
void fileReceivedAndSaved(QString url);
|
||||
|
||||
public:
|
||||
PlatformShareUtils(QObject *parent = 0);
|
||||
virtual ~PlatformShareUtils();
|
||||
virtual bool checkMimeTypeView(const QString &mimeType);
|
||||
virtual bool checkMimeTypeEdit(const QString &mimeType);
|
||||
virtual QString getTemporaryFileLocationPath();
|
||||
virtual void shareText(const QString &text, const QUrl &url);
|
||||
virtual void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
virtual void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
virtual void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
|
||||
virtual void checkPendingIntents(const QString workingDirPath);
|
||||
};
|
||||
|
||||
#endif // PLATFORMSHAREUTILS_H
|
63
headers/shareUtils/shareutils.h
Executable file
|
@ -0,0 +1,63 @@
|
|||
// (c) 2017 Ekkehard Gentz (ekke)
|
||||
// this project is based on ideas from
|
||||
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||
// also inspired by:
|
||||
// https://www.androidcode.ninja/android-share-intent-example/
|
||||
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||
// see also /COPYRIGHT and /LICENSE
|
||||
|
||||
#ifndef SHAREUTILS_H
|
||||
#define SHAREUTILS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include "shareUtils/platformshareutils.h"
|
||||
|
||||
class ShareUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
signals:
|
||||
void shareEditDone(int requestCode);
|
||||
void shareFinished(int requestCode);
|
||||
void shareNoAppAvailable(int requestCode);
|
||||
void shareError(int requestCode, QString message);
|
||||
void fileUrlReceived(QString url);
|
||||
void otherUrlReceived(QString url, QString scheme);
|
||||
void fileReceivedAndSaved(QString url);
|
||||
|
||||
public slots:
|
||||
void onShareEditDone(int requestCode);
|
||||
void onShareFinished(int requestCode);
|
||||
void onShareNoAppAvailable(int requestCode);
|
||||
void onShareError(int requestCode, QString message);
|
||||
void onFileUrlReceived(QString url);
|
||||
void onOtherUrlReceived(QString url, QString scheme);
|
||||
void onFileReceivedAndSaved(QString url);
|
||||
|
||||
public:
|
||||
explicit ShareUtils(QObject *parent = 0);
|
||||
Q_INVOKABLE bool checkMimeTypeView(const QString &mimeType);
|
||||
Q_INVOKABLE bool checkMimeTypeEdit(const QString &mimeType);
|
||||
Q_INVOKABLE QString getTemporaryFileLocationPath();
|
||||
Q_INVOKABLE void shareText(const QString &text, const QUrl &url);
|
||||
Q_INVOKABLE void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
Q_INVOKABLE void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
Q_INVOKABLE void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||
Q_INVOKABLE void checkPendingIntents(const QString workingDirPath);
|
||||
|
||||
private:
|
||||
PlatformShareUtils* mPlatformShareUtils;
|
||||
|
||||
};
|
||||
|
||||
#endif //SHAREUTILS_H
|
45
ios/Info.plist
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${QMAKE_SHORT_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>${QMAKE_PKGINFO_TYPEINFO}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${QMAKE_FULL_VERSION}</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
|
||||
<key>NOTE</key>
|
||||
<string>This file was generated by Qt/QMake.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>blueROCK would like to access the camera.</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
11
ios/blueROCK.entitlements
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:l.bluerock.dev</string>
|
||||
<string>applinks:app.bluerock.dev</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
1
qzxing
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit cfc728583b867e157bd27e8b7c239c05a081e562
|
21
resources/qml/Components/AlignedButton.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
property alias horizontalAlignment: label.horizontalAlignment
|
||||
property alias verticalAlignment: label.verticalAlignment
|
||||
|
||||
contentItem: Label {
|
||||
id: label
|
||||
|
||||
text: control.text
|
||||
font: control.font
|
||||
|
||||
color: !control.enabled ? control.Material.hintTextColor :
|
||||
control.flat && control.highlighted ? control.Material.accentColor :
|
||||
control.highlighted ? control.Material.primaryHighlightedTextColor : control.Material.foreground
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
id: toolBar
|
||||
color: "white"
|
||||
color: Material.background
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
|
|
63
resources/qml/Components/ColoredItemDelegate.qml
Normal file
|
@ -0,0 +1,63 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: http://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL3$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or later as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.GPL included in
|
||||
** the packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 2.0 requirements will be
|
||||
** met: http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Controls.Material.impl 2.15
|
||||
|
||||
ItemDelegate {
|
||||
id: control
|
||||
|
||||
Material.background: "transparent"
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: control.Material.delegateHeight
|
||||
|
||||
color: control.highlighted ? control.Material.listHighlightColor : control.Material.background
|
||||
|
||||
Ripple {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
clip: visible
|
||||
pressed: control.pressed
|
||||
anchor: control
|
||||
active: control.down || control.visualFocus || control.hovered
|
||||
color: control.Material.rippleColor
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Controls.Material.impl 2.12
|
||||
|
||||
ItemDelegate {
|
||||
ColoredItemDelegate {
|
||||
id: competitionDel
|
||||
|
||||
property bool over
|
||||
|
@ -81,15 +85,7 @@ ItemDelegate {
|
|||
NumberAnimation { target: competitionDel; property: "scale"; from: 1; to: 0.8; duration: 400 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: delBackroundRect
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0.5
|
||||
|
||||
color: control.getCompCatData(catId) === undefined ? "white":control.getCompCatData(catId)["bgcolor"]
|
||||
}
|
||||
Material.background: control.getCompCatData(catId) === undefined ? app.federalColor:control.getCompCatData(catId)["bgcolor"]
|
||||
|
||||
Column {
|
||||
id: compDelCol
|
||||
|
@ -117,14 +113,17 @@ ItemDelegate {
|
|||
|
||||
ToolButton {
|
||||
id: bookmarkTb
|
||||
icon.name: competitionDel.thisIsFavored ? "pinFilled":"pin"
|
||||
|
||||
text: "\uf005"
|
||||
font.family: competitionDel.thisIsFavored ? fa5solid.name : fa5regular.name
|
||||
|
||||
onClicked: {
|
||||
control.editFavorites(!competitionDel.thisIsFavored, parseInt(thisData['WetId']))
|
||||
}
|
||||
|
||||
Layout.alignment: Layout.Right
|
||||
|
||||
Behavior on icon.name {
|
||||
Behavior on font.family {
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
property: "scale"
|
||||
|
@ -146,8 +145,6 @@ ItemDelegate {
|
|||
Label {
|
||||
id: dateLa
|
||||
|
||||
color: "grey"
|
||||
|
||||
text: date
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,21 +64,6 @@ ListView {
|
|||
}
|
||||
}
|
||||
|
||||
InfoArea {
|
||||
id: infoArea
|
||||
|
||||
anchors {
|
||||
left: control.left
|
||||
right: control.right
|
||||
top: control.top
|
||||
margins: app.landscape() ? control.width * 0.4:control.width * 0.3
|
||||
topMargin: control.height*( status === 901 ? 0.6:0.5) - height * 0.8
|
||||
}
|
||||
|
||||
excludedCodes: [200, 902, 905]
|
||||
errorCode: control.status
|
||||
}
|
||||
|
||||
PullRefresher {
|
||||
target: control
|
||||
|
||||
|
|
|
@ -13,8 +13,15 @@ Dialog {
|
|||
x: (parent.width - width) * 0.5
|
||||
y: (parent.height - height) * 0.5
|
||||
|
||||
width: parent.width * 0.8
|
||||
height: implicitHeight
|
||||
implicitWidth: parent.width * 0.9
|
||||
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding
|
||||
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
|
||||
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
|
||||
|
||||
//width: app.width * 0.8
|
||||
//height: implicitHeight
|
||||
|
||||
modal: true
|
||||
|
||||
|
@ -41,8 +48,7 @@ Dialog {
|
|||
contentItem: Label {
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
width: control.width * 0.8
|
||||
height: implicitHeight
|
||||
width: control.parent * 0.8
|
||||
|
||||
text: control.content
|
||||
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import QtQuick.Controls.Material.impl 2.12
|
||||
|
||||
ToolButton {
|
||||
MouseArea {
|
||||
id: control
|
||||
|
||||
property string image
|
||||
|
@ -39,7 +41,7 @@ ToolButton {
|
|||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Item {
|
||||
id: controlBackgroundContainer
|
||||
|
||||
anchors.fill: parent
|
||||
|
@ -67,7 +69,7 @@ ToolButton {
|
|||
|
||||
radius: height * 0.2
|
||||
|
||||
color: control.down ? Qt.darker(control.backgroundColor, 1.03) : control.backgroundColor
|
||||
color: Material.dialogColor //control.down ? Qt.darker(control.backgroundColor, 1.03) : control.backgroundColor
|
||||
|
||||
Image {
|
||||
id: buttonIcon
|
||||
|
@ -84,22 +86,30 @@ ToolButton {
|
|||
scale: control.imageScale
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 100
|
||||
Ripple {
|
||||
id: ripple
|
||||
visible: true
|
||||
clipRadius: controlBackground.radius
|
||||
clip: true
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
pressed: control.pressed
|
||||
anchor: control
|
||||
active: control.pressed || control.visualFocus || control.containsMouse
|
||||
color: control.Material.rippleColor
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Item {
|
||||
width: ripple.width
|
||||
height: ripple.height
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: controlBackground.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: conetntText
|
||||
text: qsTr(control.text)
|
||||
anchors.centerIn: parent
|
||||
font: parent.font
|
||||
color: control.textColor
|
||||
opacity: control.enabled ? 1:0.4
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
90
resources/qml/Components/MovingLabel.qml
Normal file
|
@ -0,0 +1,90 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
property alias text: firstLabel.text
|
||||
property alias font: firstLabel.font
|
||||
property alias verticalAlignment: firstLabel.verticalAlignment
|
||||
property int spacing: 30
|
||||
|
||||
property MovingLabel syncWithLabel
|
||||
property int _spacing: syncWithLabel && syncWithLabel._labelWidth > _labelWidth ? (syncWithLabel._labelWidth - _labelWidth + syncWithLabel.spacing) : spacing
|
||||
property alias _labelWidth: firstLabel.width
|
||||
|
||||
signal linkActivated(string link)
|
||||
|
||||
clip: true
|
||||
height: firstLabel.height
|
||||
|
||||
onTextChanged: {
|
||||
_resetScroll()
|
||||
if(control.syncWithLabel)
|
||||
control.syncWithLabel._resetScroll()
|
||||
}
|
||||
|
||||
function startScroll(triggerSyncedLabel=true) {
|
||||
if(control.syncWithLabel && triggerSyncedLabel)
|
||||
control.syncWithLabel.startScroll(false)
|
||||
if(control.width < firstLabel.width && !scrollAnimation.running)
|
||||
scrollAnimation.start()
|
||||
}
|
||||
|
||||
function _resetScroll() {
|
||||
scrollAnimation.stop()
|
||||
firstLabel.anchors.leftMargin = 0
|
||||
}
|
||||
|
||||
Label {
|
||||
id: firstLabel
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
onLinkActivated: control.onLinkActivated(link)
|
||||
}
|
||||
|
||||
Label {
|
||||
id: secondLabel
|
||||
|
||||
anchors {
|
||||
left: firstLabel.right
|
||||
leftMargin: control._spacing
|
||||
verticalCenter: firstLabel.verticalCenter
|
||||
}
|
||||
|
||||
visible: scrollAnimation.running
|
||||
|
||||
font: firstLabel.font
|
||||
text: firstLabel.text
|
||||
verticalAlignment: firstLabel.verticalAlignment
|
||||
elide: firstLabel.elide
|
||||
bottomPadding: firstLabel.bottomPadding
|
||||
padding: firstLabel.padding
|
||||
|
||||
onLinkActivated: control.onLinkActivated(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: control.startScroll()
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: scrollAnimation
|
||||
target: firstLabel
|
||||
property: "anchors.leftMargin"
|
||||
from: 0
|
||||
to: -(firstLabel.width + control._spacing)
|
||||
duration: (to / -100) * 1500
|
||||
|
||||
onRunningChanged: {
|
||||
if(!running)
|
||||
control._resetScroll()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
@ -37,10 +38,6 @@ Item {
|
|||
|
||||
property double dragRefreshPositionMultiplier: 0.6 // position of the item when starting to refresh
|
||||
|
||||
property color backgroundColor: "white" // color for the pre-defined background
|
||||
property color pullIndicatorColor: "black" // color for the pre-defined pull indicator
|
||||
//property color busyIndicatorColor: "pink" // color for the pre-defined busy indicator
|
||||
|
||||
readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)
|
||||
|
||||
property Component background: Item {
|
||||
|
@ -61,7 +58,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
|
||||
radius: width * 0.5
|
||||
color: control.backgroundColor
|
||||
color: Material.dialogColor
|
||||
}
|
||||
}
|
||||
property Component busyIndicator: BusyIndicator { running: true }
|
||||
|
@ -107,7 +104,7 @@ Item {
|
|||
ctx.reset()
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.strokeStyle = control.pullIndicatorColor;
|
||||
ctx.strokeStyle = control.Material.foreground;
|
||||
|
||||
// middle line
|
||||
ctx.moveTo(width/2, topMargin);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
|
||||
ItemDelegate {
|
||||
ColoredItemDelegate {
|
||||
id: partDel
|
||||
|
||||
property int ind: index
|
||||
property var thisData: widgetData[ "participants" ][partDel.ind]
|
||||
property int thisIndex: index
|
||||
property var thisData: widgetData[ "participants" ][partDel.thisIndex]
|
||||
|
||||
width: control.width
|
||||
height: partDelCol.showSideBySide ? 40:70
|
||||
|
@ -24,23 +25,14 @@ ItemDelegate {
|
|||
app.openWidget({person:thisData["PerId"]})
|
||||
}
|
||||
|
||||
highlighted: partDel.thisIndex % 2 == 0
|
||||
|
||||
ParallelAnimation {
|
||||
id: fadeInPa
|
||||
NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
||||
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: partDelBackgroundRect
|
||||
anchors.fill: parent
|
||||
|
||||
width: partDel.width
|
||||
|
||||
color: partDel.ind % 2 == 0 ? "white":"lightgrey"
|
||||
|
||||
opacity: 0.2
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: partDelCol
|
||||
|
||||
|
@ -133,8 +125,8 @@ ItemDelegate {
|
|||
|
||||
function getDataForIcon(index){
|
||||
// TODO: clean
|
||||
var resultString = widgetData[ "participants" ][partDel.ind]["boulder"+(index+1)]
|
||||
var numTrys = widgetData[ "participants" ][partDel.ind]["try"+(index+1)]
|
||||
var resultString = widgetData[ "participants" ][partDel.thisIndex]["boulder"+(index+1)]
|
||||
var numTrys = widgetData[ "participants" ][partDel.thisIndex]["try"+(index+1)]
|
||||
|
||||
var resultList = []
|
||||
|
||||
|
@ -222,7 +214,7 @@ ItemDelegate {
|
|||
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
|
||||
|
||||
// fill
|
||||
if(resultData[0] !== -1) {
|
||||
if(resultData[0] !== -1 || resultData[2] !== -1) {
|
||||
// if there is a result available -> draw background
|
||||
context.fillStyle = "#b7b7b7";
|
||||
}
|
||||
|
@ -234,7 +226,7 @@ ItemDelegate {
|
|||
|
||||
// outline
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = '#424242';
|
||||
context.strokeStyle = Material.primaryTextColor;
|
||||
context.stroke();
|
||||
|
||||
if(resultData[1] > 0){
|
||||
|
@ -262,7 +254,7 @@ ItemDelegate {
|
|||
|
||||
// outline
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = '#424242';
|
||||
context.strokeStyle = Material.primaryTextColor;
|
||||
context.stroke();
|
||||
|
||||
|
||||
|
@ -287,7 +279,7 @@ ItemDelegate {
|
|||
|
||||
// outline
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = '#424242';
|
||||
context.strokeStyle = Material.primaryTextColor;
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +303,8 @@ ItemDelegate {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
text: boulderResCv.resultData[2]
|
||||
|
||||
}
|
||||
|
@ -336,6 +330,8 @@ ItemDelegate {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
text: boulderResCv.resultData[1]
|
||||
}
|
||||
|
||||
|
@ -360,6 +356,8 @@ ItemDelegate {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
text: boulderResCv.resultData[0]
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +425,7 @@ ItemDelegate {
|
|||
|
||||
visible: index === 0
|
||||
|
||||
color: "grey"
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -438,7 +436,7 @@ ItemDelegate {
|
|||
width: 1
|
||||
height: parent.height
|
||||
|
||||
color: "grey"
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -454,7 +452,9 @@ ItemDelegate {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])]
|
||||
text: widgetData["participants"][partDel.thisIndex]["result"+(generalResRep.routes[index][0])] === undefined ?
|
||||
"":
|
||||
widgetData[ "participants" ][partDel.thisIndex]["result"+(generalResRep.routes[index][0])]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ ItemDelegate {
|
|||
font.pixelSize: Math.abs( height * 0.6 )
|
||||
minimumPixelSize: 1
|
||||
|
||||
text: widgetData[ "participants" ][partDel.ind]["result"] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"]
|
||||
text: widgetData[ "participants" ][partDel.thisIndex]["result"] === undefined ? "":widgetData[ "participants" ][partDel.thisIndex]["result"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.3
|
||||
|
||||
Dialog {
|
||||
|
@ -7,9 +7,9 @@ Dialog {
|
|||
|
||||
property var dataObj
|
||||
property string subTitle: ""
|
||||
property int implicitY: parent.height - implicitHeight
|
||||
|
||||
signal selectionFinished(int index, var data)
|
||||
signal linkActivated(string link)
|
||||
|
||||
parent: Overlay.overlay
|
||||
|
||||
|
@ -33,38 +33,35 @@ Dialog {
|
|||
id: selectorPuHeaderCol
|
||||
|
||||
width: control.width
|
||||
height: headerSubLa.text !== "" && headerLa.text !== "" ? 73 : 40
|
||||
height: headerLa.height + headerTopSpacerItm.height + (control.subTitle ? headerSubLa.height:0)
|
||||
|
||||
Label {
|
||||
Item {
|
||||
id: headerTopSpacerItm
|
||||
height: control.padding
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
MovingLabel {
|
||||
id: headerLa
|
||||
|
||||
visible: control.title
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: selectorPuHeaderCol.width - control.padding * 2
|
||||
|
||||
width: selectorPuHeaderCol.width
|
||||
|
||||
elide: "ElideRight"
|
||||
padding: control.padding
|
||||
bottomPadding: 0
|
||||
font.bold: true
|
||||
font.pixelSize: 16
|
||||
|
||||
text: control.title
|
||||
|
||||
onLinkActivated: {
|
||||
console.log("Opening " + link)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
onLinkActivated: control.linkActivated(link)
|
||||
}
|
||||
|
||||
Label {
|
||||
id: headerSubLa
|
||||
|
||||
visible: control.subTitle
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: selectorPuHeaderCol.width - control.padding * 2
|
||||
|
||||
width: selectorPuHeaderCol.width
|
||||
|
||||
elide: "ElideRight"
|
||||
padding: control.padding
|
||||
wrapMode: Text.Wrap
|
||||
topPadding: 5
|
||||
bottomPadding: 0
|
||||
font.bold: true
|
||||
|
@ -73,8 +70,7 @@ Dialog {
|
|||
text: control.subTitle
|
||||
|
||||
onLinkActivated: {
|
||||
console.log("Opening " + link)
|
||||
Qt.openUrlExternally(link)
|
||||
control.linkActivated(link)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,6 +79,7 @@ Dialog {
|
|||
background: Item {
|
||||
Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
bottomMargin: -radius
|
||||
|
|
124
resources/qml/Components/SharePopup.qml
Normal file
|
@ -0,0 +1,124 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QZXing 3.1
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Dialog {
|
||||
id: control
|
||||
|
||||
property string _shareUrl
|
||||
property string _compName
|
||||
|
||||
parent: Overlay.overlay
|
||||
|
||||
x: (parent.width - width) * 0.5
|
||||
y: (parent.height - height) * 0.5
|
||||
|
||||
modal: true
|
||||
//% "Share these results"
|
||||
title: qsTrId("#shareResultsHeadline")
|
||||
|
||||
onClosed: {
|
||||
shareComponentLoader.sourceComponent = null
|
||||
}
|
||||
|
||||
contentItem: Loader {
|
||||
id: shareComponentLoader
|
||||
|
||||
asynchronous: false
|
||||
sourceComponent: null
|
||||
}
|
||||
|
||||
Component {
|
||||
id: shareComponent
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
currentIndex: 0
|
||||
|
||||
RowLayout {
|
||||
id: menuRow
|
||||
Repeater {
|
||||
id: buttonRepeater
|
||||
property var buttons: [
|
||||
//% "Link"
|
||||
["\uf0c1", qsTrId("#shareByLink"), serverConn.shareResultsAsUrl],
|
||||
//% "QR-Code"
|
||||
["\uf029", qsTrId("#shareByQrCode"), function() {
|
||||
stackLayout.currentIndex = 1
|
||||
}
|
||||
],
|
||||
//% "Poster"
|
||||
["\uf1c1", qsTrId("#shareByPoster"), serverConn.shareResultsAsPoster],
|
||||
]
|
||||
|
||||
model: buttons
|
||||
|
||||
delegate: Button {
|
||||
flat: true
|
||||
font.family: fa5solid.name
|
||||
text: "<font size=\"+4\">" + modelData[0] + "</font><br><br> " + modelData[1] + " "
|
||||
onClicked: buttonRepeater.buttons[index][2](_shareUrl, _compName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Image {
|
||||
id: qrCodeImage
|
||||
|
||||
property int finalSize: app.landscape() ? app.height * 0.8 : app.width * 0.8
|
||||
property int size: stackLayout.currentIndex === 1 ? finalSize:menuRow.height
|
||||
|
||||
Layout.preferredHeight: size
|
||||
Layout.preferredWidth: size
|
||||
|
||||
sourceSize.width: finalSize
|
||||
sourceSize.height: finalSize
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
source: "image://QZXing/encode/" + _shareUrl + "?border=true&correctionLevel=H"
|
||||
|
||||
RectangularGlow {
|
||||
id: effect
|
||||
anchors.fill: blurRockLogoBackgroundRect
|
||||
glowRadius: 0
|
||||
spread: 0
|
||||
opacity: 0.8
|
||||
color: "black"
|
||||
cornerRadius: blurRockLogoBackgroundRect.radius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: blurRockLogoBackgroundRect
|
||||
anchors.centerIn: parent
|
||||
width: parent.width * 0.25
|
||||
height: width
|
||||
radius: height * 0.2
|
||||
color: "white"
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width * 0.8
|
||||
height: width
|
||||
mipmap: true
|
||||
source: "qrc:/icons/blueRockHold.png"
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on size {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appear(shareUrl, compName) {
|
||||
_shareUrl = shareUrl
|
||||
_compName = compName
|
||||
shareComponentLoader.sourceComponent = shareComponent
|
||||
control.open()
|
||||
}
|
||||
}
|
|
@ -385,7 +385,7 @@ Item {
|
|||
width: parent.width
|
||||
height: roundItem.tileSize
|
||||
//scale: 0.9
|
||||
color: "white"
|
||||
color: Material.dialogColor
|
||||
border.color: "lightgrey"
|
||||
border.width: 0
|
||||
radius: height * 0.2
|
||||
|
@ -438,7 +438,7 @@ Item {
|
|||
font.bold: matchItm.winnerIndex === index
|
||||
elide: "ElideRight"
|
||||
|
||||
color: matchItm.winnerIndex === index ? "green":"black"
|
||||
color: matchItm.winnerIndex === index ? Material.color(Material.Green):Material.primaryTextColor
|
||||
|
||||
text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-"
|
||||
}
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.12
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtPurchasing 1.12
|
||||
|
||||
Rectangle {
|
||||
id: speedFlowChartLockedOverlay
|
||||
Page {
|
||||
id: control
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: -20
|
||||
|
||||
color: "white"
|
||||
|
||||
Connections {
|
||||
target: speedFlowChartProduct
|
||||
|
||||
function onPurchaseFailed() {
|
||||
purchaseBt.text = qsTr("Purchase failed")
|
||||
//% "Purchase failed"
|
||||
purchaseBt.text = qsTrId("#purchaseFailed")
|
||||
purchaseBt.enabled = false
|
||||
buttonTextResetTimer.start()
|
||||
}
|
||||
|
@ -28,62 +27,48 @@ Rectangle {
|
|||
repeat: false
|
||||
onTriggered: {
|
||||
purchaseBt.text = (speedFlowChartProduct.status === Product.Registered
|
||||
? "Buy now for " + speedFlowChartProduct.price
|
||||
: qsTr("this item is currently unavailable"))
|
||||
//% "Buy now for"
|
||||
? qsTrId("#buyNowFor") + " " + speedFlowChartProduct.price
|
||||
//% "This item is currently unavailable"
|
||||
: qsTrId("#itemIsUnavailable"))
|
||||
purchaseBt.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: lockedLayout
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: parent.height * 0.05
|
||||
topMargin: parent.height * 0.02
|
||||
bottomMargin: parent.height * 0.05
|
||||
rightMargin: parent.width * 0.1 + 20
|
||||
leftMargin: parent.width * 0.1 + 20
|
||||
}
|
||||
|
||||
//spacing: parent.height * 0.05
|
||||
spacing: lockedLayout.height * 0.01
|
||||
|
||||
Image {
|
||||
id: name
|
||||
|
||||
Layout.alignment: Layout.Center
|
||||
Layout.preferredHeight: height
|
||||
Layout.preferredWidth: width
|
||||
|
||||
width: lockedLayout.height * 0.05
|
||||
height: width
|
||||
|
||||
mipmap: true
|
||||
|
||||
source: "qrc:/icons/lock.png"
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
|
||||
height: parent.height * 0.05
|
||||
|
||||
text: qsTr("This is a premium feature.")
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: lockedLayout.height * 0.03
|
||||
width: lockedLayout.width
|
||||
//% "This is a premium feature."
|
||||
text: qsTrId("#thisIsAPremiumFeature")
|
||||
|
||||
font.pixelSize: height
|
||||
font.bold: true
|
||||
font.pixelSize: parent.height * 0.05
|
||||
fontSizeMode: Text.Fit
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
minimumPixelSize: 1
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
}
|
||||
|
||||
SwipeGallery {
|
||||
property string platformIcons: Qt.platform.os === "osx" || Qt.platform.os === "iso" ? "ios":"android"
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
height: lockedLayout.height * 0.85
|
||||
width: lockedLayout.width
|
||||
|
||||
images: [
|
||||
"qrc:/screenshots/SpeedFlowchartDemo/" + platformIcons + "/1.jpeg",
|
||||
|
@ -94,39 +79,52 @@ Rectangle {
|
|||
|
||||
Button {
|
||||
id: purchaseBt
|
||||
Layout.alignment: Layout.Center
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: lockedLayout.height * 0.07
|
||||
enabled: speedFlowChartProduct.status === Product.Registered
|
||||
text: speedFlowChartProduct.status === Product.Registered
|
||||
? "Buy now for " + speedFlowChartProduct.price
|
||||
: qsTr("This item is currently unavailable")
|
||||
icon.name: "buy"
|
||||
? "\uf218 "+ qsTrId("#buyNowFor") + " " + speedFlowChartProduct.price
|
||||
: qsTrId("#itemIsUnavailable")
|
||||
font.family: fa5solid.name
|
||||
font.pixelSize: height * 0.4
|
||||
font.capitalization: Font.MixedCase
|
||||
onClicked: speedFlowChartProduct.purchase()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: bottomRow
|
||||
|
||||
Layout.alignment: Layout.Center
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: lockedLayout.height * 0.065
|
||||
|
||||
Button {
|
||||
id: restorePurchaseButton
|
||||
Layout.alignment: Layout.Center
|
||||
visible: speedFlowChartProduct.status === Product.Registered
|
||||
Button {
|
||||
id: restorePurchaseButton
|
||||
Layout.alignment: Layout.Center
|
||||
Layout.preferredHeight: bottomRow.height
|
||||
//visible: speedFlowChartProduct.status === Product.Registered
|
||||
|
||||
flat: true
|
||||
text: "restore purchase"
|
||||
flat: true
|
||||
font.pixelSize: height * 0.4
|
||||
font.capitalization: Font.MixedCase
|
||||
//% "Restore purchase"
|
||||
text: qsTrId("#restorePurchase")
|
||||
|
||||
onClicked: inAppProductStore.restorePurchases()
|
||||
}
|
||||
onClicked: inAppProductStore.restorePurchases()
|
||||
}
|
||||
|
||||
Button {
|
||||
id: contactSupportButton
|
||||
Layout.alignment: Layout.Center
|
||||
Button {
|
||||
id: contactSupportButton
|
||||
Layout.alignment: Layout.Center
|
||||
Layout.preferredHeight: bottomRow.height
|
||||
|
||||
flat: true
|
||||
text: "contact support"
|
||||
flat: true
|
||||
font.pixelSize: height * 0.4
|
||||
font.capitalization: Font.MixedCase
|
||||
//% "contact support"
|
||||
text: qsTrId("#contact support")
|
||||
|
||||
onClicked: Qt.openUrlExternally("mailto:contact@itsblue.de")
|
||||
}
|
||||
onClicked: Qt.openUrlExternally("mailto:contact@itsblue.de")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ Rectangle {
|
|||
//property bool unlocked: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
|
||||
property bool unlocked: appSettings.read("speedBackendPurchase") === "1"
|
||||
|
||||
Component.onCompleted: {
|
||||
console.warn("unlocked:", appSettings.read("speedBackendPurchase"))
|
||||
}
|
||||
|
||||
state: "hidden"
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
anchors.margins: 1
|
||||
anchors.bottomMargin: indicator.height
|
||||
spacing: width * 0.2
|
||||
|
||||
Repeater {
|
||||
model: control.images.length
|
||||
|
|
298
resources/qml/Pages/QrCodeScanPage.qml
Normal file
|
@ -0,0 +1,298 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QZXing 3.1
|
||||
import QtMultimedia 5.12
|
||||
import QtQuick.Shapes 1.12
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
||||
import "../Components"
|
||||
|
||||
Page {
|
||||
id: control
|
||||
|
||||
property string _statusText: ""
|
||||
property string _statusColor: Material.primaryTextColor
|
||||
property bool _freezeScanning: false
|
||||
signal headerComponentChanged()
|
||||
|
||||
//% "Scan QR-Code"
|
||||
title: qsTrId("#scanQrCode")
|
||||
|
||||
onFocusChanged: focus ? open() : close()
|
||||
|
||||
function open() {
|
||||
_setDefaultStatusText()
|
||||
control._freezeScanning = false
|
||||
if(serverConn.isCameraPermissionGranted())
|
||||
cameraLoader.sourceComponent = cameraComponent
|
||||
else
|
||||
cameraLoader.sourceComponent = noPermissionComponent
|
||||
}
|
||||
|
||||
function close() {
|
||||
cameraLoader.sourceComponent = null
|
||||
}
|
||||
|
||||
function _setDefaultStatusText() {
|
||||
//% "Place the Code in the center"
|
||||
_statusText = qsTrId("#placeQrCodeInCenter")
|
||||
_statusColor = Material.primaryTextColor
|
||||
}
|
||||
|
||||
function _handleTag(tag) {
|
||||
if(control._freezeScanning)
|
||||
return
|
||||
|
||||
control._freezeScanning = true
|
||||
|
||||
//% "Plase wait"
|
||||
control._statusText = qsTrId("#pleaseWait") + "..."
|
||||
|
||||
if(app.openWidgetFromUrl(tag))
|
||||
control.close()
|
||||
else {
|
||||
//% "Invalid QR-Code"
|
||||
control._statusText = qsTrId("#invalidQrCode")
|
||||
control._statusColor = Material.color(Material.Red)
|
||||
statusTextResetTimer.start()
|
||||
control._freezeScanning = false
|
||||
}
|
||||
}
|
||||
|
||||
function _requestCameraPermission() {
|
||||
var permissionGranted = serverConn.requestCameraPermission()
|
||||
|
||||
if(permissionGranted)
|
||||
cameraLoader.sourceComponent = cameraComponent
|
||||
}
|
||||
|
||||
contentItem: Loader {
|
||||
id: cameraLoader
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
//asynchronous: true
|
||||
sourceComponent: null
|
||||
}
|
||||
|
||||
Component {
|
||||
id: cameraComponent
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Camera {
|
||||
id: camera
|
||||
captureMode: Camera.CaptureStillImage
|
||||
imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceAuto
|
||||
|
||||
focus {
|
||||
focusMode: Camera.FocusContinuous
|
||||
focusPointMode: Camera.FocusPointCenter
|
||||
}
|
||||
}
|
||||
|
||||
FancyBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
VideoOutput {
|
||||
id: videoOutput
|
||||
x: 0
|
||||
y: 0
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
fillMode: VideoOutput.PreserveAspectCrop
|
||||
|
||||
source: camera
|
||||
filters: [ zxingFilter ]
|
||||
focus : visible // to receive focus and capture key events when visible
|
||||
|
||||
autoOrientation: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
if (camera.lockStatus !== Camera.Unlocked)
|
||||
camera.unlock();
|
||||
|
||||
camera.searchAndLock();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: app.landscape() ? focusIndicatorRect.left : parent.right
|
||||
bottom: app.landscape() ? parent.bottom : focusIndicatorRect.top
|
||||
}
|
||||
|
||||
opacity: focusIndicatorRect.opacity
|
||||
color: focusIndicatorRect.border.color
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: focusIndicatorRect
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: Math.min(parent.height, parent.width)
|
||||
height: width
|
||||
|
||||
border.width: width * 0.1
|
||||
border.color: "#000000"
|
||||
|
||||
opacity: 0.5
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
bottom: focusIndicatorRect.bottom
|
||||
bottomMargin: height * 0.5
|
||||
horizontalCenter: focusIndicatorRect.horizontalCenter
|
||||
}
|
||||
|
||||
width: (focusIndicatorRect.width - focusIndicatorRect.border.width * 2) * 0.8
|
||||
height: focusIndicatorRect.border.width
|
||||
|
||||
radius: height * 0.3
|
||||
|
||||
color: Material.backgroundColor
|
||||
|
||||
Material.elevation: 10
|
||||
|
||||
|
||||
Label {
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: height * 0.1
|
||||
}
|
||||
|
||||
color: control._statusColor
|
||||
|
||||
font.pixelSize: height * 0.5
|
||||
fontSizeMode: Text.Fit
|
||||
minimumPixelSize: height * 0.2
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: control._statusText
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: app.landscape() ? parent.top : focusIndicatorRect.bottom
|
||||
left: app.landscape() ? focusIndicatorRect.right : parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
opacity: focusIndicatorRect.opacity
|
||||
color: focusIndicatorRect.border.color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: noPermissionComponent
|
||||
ColumnLayout {
|
||||
//anchors.fill: parent
|
||||
//anchors.margins: app.landscape() ? app.height * 0.1 : app.width * 0.1
|
||||
|
||||
property int columnWidth: control.width * 0.9
|
||||
|
||||
spacing: height * 0.02
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Label {
|
||||
id: noPermissionIcon
|
||||
|
||||
Layout.preferredWidth: parent.columnWidth
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
font.pixelSize: app.landscape() ? parent.height * 0.25:parent.height * 0.15
|
||||
font.family: fa5solid.name
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: "\uf3ed"
|
||||
}
|
||||
Label {
|
||||
id: noPermissionText
|
||||
|
||||
Layout.preferredWidth: parent.columnWidth
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
font.bold: true
|
||||
font.pixelSize: noPermissionIcon.height * 0.2
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
//% "Camera access required"
|
||||
text: qsTrId("#cameraPermissionDenied")
|
||||
}
|
||||
|
||||
Label {
|
||||
id: noPermissionDetailText
|
||||
|
||||
Layout.preferredWidth: parent.columnWidth
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
font.pixelSize: noPermissionText.font.pixelSize * 0.7
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
//% "blueROCK needs to access your camera in order to scan QR-Codes. It will never record or store any photos or videos."
|
||||
text: qsTrId("#cameraPermissionDeniedDetails")
|
||||
}
|
||||
|
||||
Button {
|
||||
id: grantPermissionButton
|
||||
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
//% "Allow access"
|
||||
text: qsTrId("#allowAccess")
|
||||
|
||||
onClicked: control._requestCameraPermission()
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
QZXingFilter {
|
||||
id: zxingFilter
|
||||
|
||||
decoder {
|
||||
onTagFound: control._handleTag(tag)
|
||||
|
||||
enabledDecoders: QZXing.DecoderFormat_QR_CODE
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: statusTextResetTimer
|
||||
running: false
|
||||
repeat: false
|
||||
interval: 3000
|
||||
onTriggered: _setDefaultStatusText()
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
||||
import "../Components"
|
||||
|
||||
|
@ -37,11 +38,10 @@ Page {
|
|||
topMargin: root.height * 0.03
|
||||
}
|
||||
|
||||
height: menuGr.buttonSize * 0.3
|
||||
spacing: anchors.topMargin * 0.5
|
||||
height: app.landscape() ? menuGr.buttonSize * 0.2:menuGr.buttonSize * 0.3
|
||||
}
|
||||
|
||||
Grid {
|
||||
GridLayout {
|
||||
id: menuGr
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
@ -49,17 +49,19 @@ Page {
|
|||
rows: app.landscape() ? 1:2
|
||||
columns: app.landscape() ? 2:1
|
||||
|
||||
spacing: !app.landscape() ? parent.height * 0.08:parent.width * 0.1
|
||||
rowSpacing: app.landscape() ? parent.width * 0.1:headerBadge.anchors.topMargin
|
||||
columnSpacing: rowSpacing
|
||||
|
||||
property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.2
|
||||
property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.19
|
||||
|
||||
FancyButton {
|
||||
id: davBt
|
||||
|
||||
height: menuGr.buttonSize
|
||||
width: height
|
||||
Layout.preferredHeight: menuGr.buttonSize
|
||||
Layout.preferredWidth: menuGr.buttonSize
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
image: "qrc:/icons/dav.png"
|
||||
image: Material.theme === Material.Dark ? "qrc:/icons/dav-dark.png":"qrc:/icons/dav.png"
|
||||
|
||||
onClicked: {
|
||||
app.openWidget({nation:"GER"})
|
||||
|
@ -70,90 +72,107 @@ Page {
|
|||
FancyButton {
|
||||
id: sacBt
|
||||
|
||||
height: menuGr.buttonSize
|
||||
width: height
|
||||
Layout.preferredHeight: menuGr.buttonSize
|
||||
Layout.preferredWidth: menuGr.buttonSize
|
||||
Layout.alignment: Layout.Center
|
||||
|
||||
image: "qrc:/icons/sac.png"
|
||||
image: Material.theme === Material.Dark ? "qrc:/icons/sac-dark.png":"qrc:/icons/sac.png"
|
||||
|
||||
onClicked: {
|
||||
app.openWidget({nation:"SUI"})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: bottomDigitalrockDisclaimerLabel.top
|
||||
}
|
||||
|
||||
Button {
|
||||
id: ifscDisclaimerButton
|
||||
|
||||
flat: true
|
||||
font.bold: true
|
||||
font.pixelSize: aboutBluerockDisclaimerButton.font.pixelSize
|
||||
|
||||
text: "Where are the IFSC results?"
|
||||
|
||||
onClicked: ifscDisclaimerDialog.open()
|
||||
}
|
||||
|
||||
Button {
|
||||
id: aboutBluerockDisclaimerButton
|
||||
|
||||
flat: true
|
||||
font.pixelSize: bottomDigitalrockDisclaimerLabel.paintedHeight * (Qt.platform.os === "android" ? 0.8:0.735)
|
||||
|
||||
text: "About blueROCK"
|
||||
|
||||
onClicked: aboutBluerockDisclaimerDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: bottomDigitalrockDisclaimerLabel
|
||||
Grid {
|
||||
id: footerMenu
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: headerBadge.anchors.topMargin
|
||||
margins: headerBadge.anchors.topMargin
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
width: parent.width * 0.9
|
||||
height: anchors.bottomMargin
|
||||
width: app.landscape() ? childrenRect.width : parent.width * 0.8
|
||||
height: app.landscape() ? headerBadge.height : Math.min(headerBadge.height * 2, width * 0.27)
|
||||
|
||||
fontSizeMode: Label.Fit
|
||||
minimumPixelSize: 1
|
||||
columnSpacing: height * 0.1
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
columns: app.landscape() ? 4:2
|
||||
rows: app.landscape() ? 1:2
|
||||
|
||||
text: "Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
||||
Repeater {
|
||||
id: buttonRepeater
|
||||
property var buttons: [
|
||||
//% "IFSC results"
|
||||
["\uf059", qsTrId("#ifscResults"), ifscDisclaimerDialog.open],
|
||||
[
|
||||
"\uf042",
|
||||
Material.theme === Material.Light ?
|
||||
//% "Dark mode"
|
||||
qsTrId("#darkMode"):
|
||||
//% "Light mode"
|
||||
qsTrId("#lightMode"),
|
||||
app.toggleDarkMode
|
||||
],
|
||||
//% "About blueROCK"
|
||||
["\uf05a", qsTrId("#aboutBluerock"), aboutBluerockDisclaimerDialog.open],
|
||||
["\uf029", qsTrId("#scanQrCode"), function(){
|
||||
mainStack.push("qrc:/Pages/QrCodeScanPage.qml")
|
||||
}],
|
||||
]
|
||||
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
model: buttons
|
||||
|
||||
delegate: Item {
|
||||
|
||||
width: app.landscape() ? footerMenuButton.implicitWidth : footerMenu.width * 0.5 - (footerMenu.columnSpacing / 2)
|
||||
height: app.landscape() ? footerMenu.height : footerMenu.height * 0.5 - (footerMenu.rowSpacing / 2)
|
||||
|
||||
Button {
|
||||
id: footerMenuButton
|
||||
|
||||
property bool isLeft: index % 2 === 0
|
||||
|
||||
anchors {
|
||||
right: isLeft && !app.landscape() ? parent.right : undefined
|
||||
left: isLeft && !app.landscape() ? undefined : parent.left
|
||||
centerIn: app.landscape() ? parent : undefined
|
||||
}
|
||||
|
||||
height: parent.height
|
||||
|
||||
flat: true
|
||||
|
||||
font.family: fa5solid.name
|
||||
font.pixelSize: height * 0.4
|
||||
font.capitalization: Font.MixedCase
|
||||
//horizontalAlignment: isLeft ? Text.AlignRight : Text.AlignLeft
|
||||
|
||||
text: isLeft && !app.landscape() ? modelData[1] + " " + modelData[0] : modelData[0] + " " + modelData[1]
|
||||
|
||||
onClicked: buttonRepeater.buttons[index][2]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DisclaimerDialog {
|
||||
id: ifscDisclaimerDialog
|
||||
title: "Where are the IFSC results?"
|
||||
content: "Unfortunately, the IFSC has restricted the access to their data and <b>is not willing to share results with blueROCK anymore</b>. " +
|
||||
"Because of this, blueROCK is no longer able to access and display IFSC results.<br><br>" +
|
||||
"You can find current IFSC results <a href=\"https://ifsc.results.info\">over here</a> and on <a href=\"https://ifsc-climbing.org\">their website</a>."
|
||||
Material.theme: root.Material.theme
|
||||
//% "Where are the IFSC results?"
|
||||
title: qsTrId("#ifscDisclaimerTitle")
|
||||
//% "Unfortunately, the IFSC has restricted the access to their data and <b>is not willing to share results with blueROCK anymore</b>. Because of this, blueROCK is no longer able to access and display IFSC results.<br><br>You can find current IFSC results <a href=\"https://ifsc.results.info\">over here</a> and on <a href=\"https://ifsc-climbing.org\">their website</a>."
|
||||
content: qsTrId("#ifscDisclaimer")
|
||||
}
|
||||
|
||||
DisclaimerDialog {
|
||||
id: aboutBluerockDisclaimerDialog
|
||||
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>"
|
||||
content: "This app was built using the <a href='https://qt.io'>Qt Framework</a> " +
|
||||
"licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>"+
|
||||
|
||||
"This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>," +
|
||||
"the source code can be found <a href='https://itsblue.dev/dorian/blueROCK/'>here</a>."
|
||||
|
||||
Material.theme: root.Material.theme
|
||||
//% "privacy policy"
|
||||
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>, <a href=\"https://itsblue.de/apps/bluerock\">" + qsTrId("#privacyPolicy") + "</a>"
|
||||
//% "This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/blueROCK/app'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
||||
content: qsTrId("#aboutBluerockDisclaimer")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ Page {
|
|||
Result,
|
||||
|
||||
Ranking,
|
||||
Aggregated // not yet implemented
|
||||
Aggregated, // not yet implemented
|
||||
|
||||
Invalid
|
||||
}
|
||||
|
||||
title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:""
|
||||
|
@ -72,7 +74,6 @@ Page {
|
|||
// route: (int) round
|
||||
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
|
||||
//}
|
||||
|
||||
var ret = serverConn.getWidgetData(params)
|
||||
|
||||
root.status = ret["status"]
|
||||
|
@ -80,7 +81,11 @@ Page {
|
|||
if(ret["status"] === 200){
|
||||
root.widgetData = ret["data"]
|
||||
root.widgetType = checkWidgetType(params, root.widgetData)
|
||||
if(widgetLd.load()){
|
||||
if(widgetType === WidgetPage.WidgetType.Invalid) {
|
||||
root.ready = false
|
||||
root.status = 906
|
||||
}
|
||||
else if(widgetLd.load()){
|
||||
root.ready = true
|
||||
}
|
||||
else {
|
||||
|
@ -88,7 +93,13 @@ Page {
|
|||
root.ready = false
|
||||
}
|
||||
}
|
||||
else if(ret["status"] === 404 && [WidgetPage.WidgetType.Registration, WidgetPage.WidgetType.Startlist, WidgetPage.WidgetType.Result].includes(root.widgetType) && root.params["route"] !== "") {
|
||||
else if(ret["status"] === 404 &&
|
||||
[
|
||||
WidgetPage.WidgetType.Registration,
|
||||
WidgetPage.WidgetType.Startlist,
|
||||
WidgetPage.WidgetType.Result
|
||||
].includes(root.widgetType) &&
|
||||
root.params["route"] !== "") {
|
||||
// if we get a 404 and have startlist, results or registration, the route was not found -> remove round and try again
|
||||
root.params["route"] = ""
|
||||
loadData(root.params)
|
||||
|
@ -118,6 +129,10 @@ Page {
|
|||
|
||||
}
|
||||
|
||||
function areParamsValid() {
|
||||
|
||||
}
|
||||
|
||||
function checkWidgetType(params, widgetData){
|
||||
var widgetType
|
||||
|
||||
|
@ -164,10 +179,25 @@ Page {
|
|||
// aggregated
|
||||
widgetType = WidgetPage.WidgetType.Aggregated
|
||||
}
|
||||
else {
|
||||
widgetType = WidgetPage.WidgetType.Invalid
|
||||
}
|
||||
|
||||
return widgetType
|
||||
}
|
||||
|
||||
function encodeQueryData(data) {
|
||||
const ret = [];
|
||||
for (let d in data)
|
||||
ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
|
||||
return ret.join('&');
|
||||
}
|
||||
|
||||
function shareWidget(compName) {
|
||||
var url = "https://l.bluerock.dev/?" + encodeQueryData(params)
|
||||
sharePu.appear(url, compName)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: widgetLd
|
||||
|
||||
|
@ -201,9 +231,6 @@ Page {
|
|||
delete(widgetLd.sourceComponent)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
function getFile(widgetType) {
|
||||
|
@ -248,11 +275,14 @@ Page {
|
|||
SelectorPopup {
|
||||
id: selectorPu
|
||||
|
||||
Material.theme: root.Material.theme
|
||||
|
||||
contentItem: ListView {
|
||||
id: selectorLv
|
||||
|
||||
property int delegateHeight: 50
|
||||
spacing: 10
|
||||
clip: true
|
||||
|
||||
implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model
|
||||
|
||||
|
@ -267,7 +297,6 @@ Page {
|
|||
leftMargin: 3
|
||||
bottom: selectorLv.bottom
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delegate: Button {
|
||||
|
@ -287,4 +316,10 @@ Page {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharePopup {
|
||||
id: sharePu
|
||||
|
||||
Material.theme: root.Material.theme
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
||||
import "../Components"
|
||||
|
||||
|
@ -27,7 +28,8 @@ DataListView {
|
|||
|
||||
property bool ready
|
||||
|
||||
property string title: (params.nation === "ICC" ? "IFSC":params.nation === "GER" ? "DAV":"SAC") + " " + qsTr("calendar") + " " + control.year
|
||||
//% "calendar"
|
||||
property string title: (params.nation === "ICC" ? "IFSC":params.nation === "GER" ? "DAV":"SAC") + " " + qsTrId("#calendar") + " " + control.year
|
||||
|
||||
property Component headerComponent: RowLayout {
|
||||
|
||||
|
@ -41,7 +43,9 @@ DataListView {
|
|||
control.changeYear()
|
||||
}
|
||||
|
||||
icon.name: "year"
|
||||
text: "\uf133"
|
||||
font.family: fa5solid.name
|
||||
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
|
@ -60,16 +64,19 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
compCats.push( {"text": qsTr("Pinned"), "data": {"sort_rank":0, "cat_id":[-1]}} )
|
||||
//% "Favorites"
|
||||
compCats.push( {"text": qsTrId("#favorites"), "data": {"sort_rank":0, "cat_id":[-1]}} )
|
||||
|
||||
compCats.sort(function(a, b) {
|
||||
return a['data']['sort_rank'] - b['data']['sort_rank'];
|
||||
});
|
||||
|
||||
filterSelectPu.appear(compCats, qsTr("Select Filters"), "")
|
||||
//% "Select filters"
|
||||
filterSelectPu.appear(compCats, qsTrId("#selectFilters"), "")
|
||||
}
|
||||
|
||||
icon.name: "filter"
|
||||
text: "\uf0b0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
|
@ -79,7 +86,8 @@ DataListView {
|
|||
control.openCup()
|
||||
}
|
||||
|
||||
icon.name: "cup"
|
||||
text: "\uf091"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,10 +113,15 @@ DataListView {
|
|||
initFilters()
|
||||
initFavorites()
|
||||
|
||||
if(model){
|
||||
if(model && widgetData["competitions"].length > 0){
|
||||
control.status = 200
|
||||
control.ready = true
|
||||
}
|
||||
else if(widgetData["competitions"].length === 0) {
|
||||
control.status = 404
|
||||
control.ready = false
|
||||
}
|
||||
|
||||
else {
|
||||
control.ready = false
|
||||
control.status = 901
|
||||
|
@ -192,13 +205,18 @@ DataListView {
|
|||
var infoUrls = getCompInfoUrls(compIndex)
|
||||
var infosheet = "";
|
||||
if(infoUrls.length >= 1)
|
||||
infosheet += ("<a href='" + getCompInfoUrls(compIndex)[0] + "'>" + qsTr('infosheet') + "</a>")
|
||||
//% "Infosheet"
|
||||
infosheet += ("<a href='" + getCompInfoUrls(compIndex)[0] + "'>" + qsTrId("#infosheet") + "</a>")
|
||||
if(infoUrls.length === 2)
|
||||
infosheet += (", <a href='" + getCompInfoUrls(compIndex)[1] + "'>" + qsTr('further infos') + "</a>")
|
||||
//% "Further infos"
|
||||
infosheet += (", <a href='" + getCompInfoUrls(compIndex)[1] + "'>" + qsTrId("#furtherInfos") + "</a>")
|
||||
|
||||
console.log("Infosheet: " + infosheet)
|
||||
|
||||
var eventWebsite = control.widgetData["competitions"][compIndex]["homepage"] !== undefined ? ("<a href='" + control.widgetData["competitions"][compIndex]["homepage"] + "'>" + qsTr('Event Website') + "</a>"):""
|
||||
|
||||
var eventWebsite = control.widgetData["competitions"][compIndex]["homepage"] !== undefined ?
|
||||
//% "Event website"
|
||||
("<a href='" + control.widgetData["competitions"][compIndex]["homepage"] + "'>" + qsTrId("#eventWebsite") + "</a>"):""
|
||||
|
||||
selector.appear(selectOptions, control.widgetData["competitions"][compIndex]['name'], eventWebsite + ((eventWebsite !== "" && infosheet !== "") ? ", ":"") + infosheet )
|
||||
}
|
||||
|
@ -214,7 +232,8 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
selector.appear(selectOptions, qsTr("select year"))
|
||||
//% "Select year"
|
||||
selector.appear(selectOptions, qsTrId("#selectYear"))
|
||||
}
|
||||
|
||||
function openCup(state, data) {
|
||||
|
@ -226,7 +245,8 @@ DataListView {
|
|||
|
||||
if(state === undefined){
|
||||
// opened for the first time -> select cup
|
||||
selectTitle = qsTr("select cup")
|
||||
//% "Select cup"
|
||||
selectTitle = qsTrId("#selectCup")
|
||||
|
||||
cups.sort(function(a, b) {
|
||||
return parseInt(b["SerId"]) - parseInt(a["SerId"]);
|
||||
|
@ -254,7 +274,8 @@ DataListView {
|
|||
return
|
||||
}
|
||||
|
||||
selectTitle = cup['name'] + ": " + qsTr("select category")
|
||||
//% "Select category"
|
||||
selectTitle = cup['name'] + ": " + qsTrId("#selectCategory")
|
||||
|
||||
// build a list with all cat in the cup out of the cat keys (rkey) given in the cup.cats
|
||||
for(prop in cup['cats']){
|
||||
|
@ -383,6 +404,10 @@ DataListView {
|
|||
app.openWidget({cup: data.cup, cat: data.cat})
|
||||
}
|
||||
}
|
||||
|
||||
function onLinkActivated(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
header: Item {
|
||||
|
@ -403,12 +428,13 @@ DataListView {
|
|||
SelectorPopup {
|
||||
id: filterSelectPu
|
||||
|
||||
Material.theme: control.Material.theme
|
||||
|
||||
contentItem: ListView {
|
||||
id: selectorLv
|
||||
|
||||
property int delegateHeight: 50
|
||||
spacing: 10
|
||||
|
||||
clip: true
|
||||
|
||||
implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model
|
||||
|
|
|
@ -35,6 +35,15 @@ Page {
|
|||
|
||||
property var widgetData: currentWidgetData
|
||||
|
||||
property Component headerComponent: ToolButton {
|
||||
id: shareToolBt
|
||||
|
||||
onClicked: shareWidget(control.title)
|
||||
|
||||
text: "\uf1e0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
control.ready = true
|
||||
control.status = 200
|
||||
|
@ -190,7 +199,8 @@ Page {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: qsTr("age") + ": " + widgetData["age"]
|
||||
//% "Age"
|
||||
text: qsTrId("#age") + ": " + widgetData["age"]
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -206,7 +216,8 @@ Page {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: qsTr("year of birth") + ": " + widgetData["birthdate"]
|
||||
//% "Year of birth"
|
||||
text: qsTrId("#yearOfBirth") + ": " + widgetData["birthdate"]
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -222,7 +233,8 @@ Page {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: qsTr("city") + ": " + widgetData["city"]
|
||||
//% "City"
|
||||
text: qsTrId("#city") + ": " + widgetData["city"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +263,7 @@ Page {
|
|||
height: 1
|
||||
width: parent.width
|
||||
|
||||
color: "black"
|
||||
color: Material.foreground
|
||||
|
||||
}
|
||||
|
||||
|
@ -263,7 +275,11 @@ Page {
|
|||
|
||||
flat: true
|
||||
|
||||
text: bestResultsRep.showAllResults ? qsTr("show best results"):qsTr("show all results")
|
||||
text: bestResultsRep.showAllResults ?
|
||||
//% "Show best results"
|
||||
qsTrId("#showBestResults"):
|
||||
//% "Show all results"
|
||||
qsTrId("#showAllResults")
|
||||
|
||||
onClicked: {
|
||||
bestResultsRep.showAllResults = !bestResultsRep.showAllResults
|
||||
|
|
|
@ -27,12 +27,22 @@ DataListView {
|
|||
property bool ready
|
||||
|
||||
property string title: control.widgetData['comp_name']
|
||||
property string subTitle: qsTr("(Ranking)") + " after " + control.widgetData['route_name']
|
||||
//% "(Ranking)"
|
||||
property string subTitle: qsTrId("#rankingHeadline") + " after " + control.widgetData['route_name']
|
||||
property bool titleIsPageTitle: true
|
||||
|
||||
property var widgetData: currentWidgetData
|
||||
signal closeAll()
|
||||
|
||||
property Component headerComponent: ToolButton {
|
||||
id: shareToolBt
|
||||
|
||||
onClicked: shareWidget(control.title)
|
||||
|
||||
text: "\uf1e0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: selector
|
||||
function onSelectionFinished(index, data) {
|
||||
|
@ -80,7 +90,7 @@ DataListView {
|
|||
}
|
||||
|
||||
onPressAndHold: {
|
||||
app.openWidget({person:thisData["PerId"]})
|
||||
app.openWidget({person:thisData["PerId"]})
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
|
@ -91,6 +101,8 @@ DataListView {
|
|||
|
||||
text: ""
|
||||
|
||||
highlighted: partDel.thisIndex % 2 == 0
|
||||
|
||||
onClicked: {
|
||||
if(state === "closed"){
|
||||
// close all other delegates
|
||||
|
@ -110,16 +122,6 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
width: partDel.width
|
||||
|
||||
color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey"
|
||||
|
||||
opacity: 0.2
|
||||
}
|
||||
|
||||
Column {
|
||||
id: partDelCol
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import "../Components"
|
||||
|
||||
|
@ -30,17 +31,34 @@ DataListView {
|
|||
property string subTitle: getSubtitle()
|
||||
property bool titleIsPageTitle: true
|
||||
|
||||
property Component headerComponent: ToolButton {
|
||||
id: moreToolBt
|
||||
property Component headerComponent: RowLayout {
|
||||
|
||||
onClicked: {
|
||||
control.changeCat()
|
||||
height: parent.height
|
||||
spacing: 0
|
||||
|
||||
ToolButton {
|
||||
id: shareToolBt
|
||||
|
||||
onClicked: shareWidget(control.title)
|
||||
|
||||
text: "\uf1e0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
icon.name: "menu"
|
||||
ToolButton {
|
||||
id: moreToolBt
|
||||
|
||||
onClicked: {
|
||||
control.changeCat()
|
||||
}
|
||||
|
||||
text: "\uf142"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
}
|
||||
|
||||
property var widgetData: currentWidgetData
|
||||
property var athletes
|
||||
|
||||
function getSubtitle() {
|
||||
var titleString
|
||||
|
@ -55,18 +73,16 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
var addition = "(Registration) "
|
||||
//% "(Registration)"
|
||||
var addition = qsTrId("#registrationHeadline")
|
||||
|
||||
if(titleString !== undefined){
|
||||
if(titleString)
|
||||
return addition + " " + titleString
|
||||
}
|
||||
else {
|
||||
else
|
||||
return ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getText(index){
|
||||
function getText(athleteData){
|
||||
|
||||
var fedName // federation name
|
||||
|
||||
|
@ -75,21 +91,20 @@ DataListView {
|
|||
|
||||
for(var i = 0; i < widgetData["federations"].length; i ++ ){
|
||||
//console.log("checking " + i + ": cat: " + parseInt(widgetData["categorys"][i]["GrpId"]) + " searched cat: " + root.catId)
|
||||
if(widgetData["federations"][i]["fed_id"] === widgetData[ 'athletes' ][index]["reg_fed_id"]){
|
||||
if(widgetData["federations"][i]["fed_id"] === athleteData["reg_fed_id"]){
|
||||
fedName = widgetData["federations"][i]["shortcut"]
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// an international competition -> get nation
|
||||
|
||||
fedName = widgetData[ 'athletes' ][index]["nation"]
|
||||
fedName = athleteData["nation"]
|
||||
}
|
||||
|
||||
return widgetData[ "athletes" ][index]["firstname"] + " " + widgetData[ "athletes" ][index]["lastname"] + " (" + fedName + ")"
|
||||
return athleteData["firstname"] + " " + athleteData["lastname"] + " (" + fedName + ")"
|
||||
}
|
||||
|
||||
function changeCat(){
|
||||
function changeCat() {
|
||||
var cats = control.widgetData["categorys"]
|
||||
|
||||
cats.sort(function(a, b) {
|
||||
|
@ -106,7 +121,22 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
selector.appear(selectOptions, qsTr("select cat"))
|
||||
selector.appear(selectOptions, qsTrId("#selectCategory"),
|
||||
//% "Show results"
|
||||
"<a href=\"blank\">" + qsTrId("#showResults") + "</a>")
|
||||
}
|
||||
|
||||
function filterAthletes(athletes) {
|
||||
if(!params.cat) {
|
||||
params.cat = control.widgetData["categorys"][0]["GrpId"]
|
||||
}
|
||||
|
||||
var filtered = athletes.filter(function(athlete){
|
||||
var res = String(athlete.cat).toLowerCase() === String(params.cat).toLowerCase()
|
||||
return res
|
||||
})
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -116,14 +146,18 @@ DataListView {
|
|||
updateData({cat: data.cat}, true)
|
||||
}
|
||||
}
|
||||
|
||||
function onLinkActivated(link) {
|
||||
selector.close()
|
||||
var tmpParams = params
|
||||
tmpParams.type = ""
|
||||
app.openWidget(tmpParams)
|
||||
}
|
||||
}
|
||||
|
||||
status: model === 0 ? 901:200
|
||||
|
||||
model: widgetData[ 'athletes' ] === undefined ? 0:widgetData[ 'athletes' ].length
|
||||
|
||||
Component.onCompleted: {
|
||||
if(model > 0){
|
||||
athletes = filterAthletes(widgetData["athletes"])
|
||||
if(athletes.length > 0){
|
||||
control.ready = true
|
||||
control.status = 200
|
||||
}
|
||||
|
@ -137,18 +171,24 @@ DataListView {
|
|||
updateData({}, false)
|
||||
}
|
||||
|
||||
onWidgetDataChanged: {
|
||||
athletes = []
|
||||
athletes = filterAthletes(widgetData["athletes"])
|
||||
}
|
||||
|
||||
model: athletes
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: partDel
|
||||
|
||||
property int thisIndex: index
|
||||
property var thisData: widgetData[ "athletes" ][index]
|
||||
|
||||
width: parent.width
|
||||
height: parseInt(thisData.cat) === parseInt(params.cat) ? 50:0
|
||||
property var thisData: modelData
|
||||
|
||||
opacity: 0
|
||||
scale: 0.9
|
||||
|
||||
width: control.width
|
||||
|
||||
onThisDataChanged: {
|
||||
fadeInPa.start()
|
||||
}
|
||||
|
@ -165,15 +205,7 @@ DataListView {
|
|||
|
||||
text: ""
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
width: partDel.width
|
||||
|
||||
color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey"
|
||||
|
||||
opacity: 0.2
|
||||
}
|
||||
highlighted: partDel.thisIndex % 2 == 0
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
|
@ -186,7 +218,7 @@ DataListView {
|
|||
|
||||
elide: "ElideRight"
|
||||
|
||||
text: control.getText(index)
|
||||
text: control.getText(partDel.thisData)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,8 @@ DataListView {
|
|||
|
||||
onClicked: control.changeCat()
|
||||
|
||||
icon.name: "menu"
|
||||
text: "\uf142"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
|
@ -61,7 +62,17 @@ DataListView {
|
|||
speedFlowChartPopup.toggle()
|
||||
}
|
||||
|
||||
icon.name: "flowchart"
|
||||
text: "\uf0e8"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
id: shareToolBt
|
||||
|
||||
onClicked: shareWidget(control.title)
|
||||
|
||||
text: "\uf1e0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,25 +124,15 @@ DataListView {
|
|||
}
|
||||
|
||||
function getSubtitle() {
|
||||
var titleString
|
||||
var titleString = control.widgetData["route_name"]
|
||||
|
||||
for(var i = 0; i < control.widgetData["categorys"].length; i ++ ){
|
||||
//console.log("checking " + i + ": cat: " + parseInt(control.widgetData["categorys"][i]["GrpId"]) + " searched cat: " + params.cat)
|
||||
if(parseInt(control.widgetData["categorys"][i]["GrpId"]) === parseInt(params.cat)){
|
||||
titleString = control.widgetData["categorys"][i]["name"]
|
||||
}
|
||||
}
|
||||
//% "(Results)"
|
||||
var addition = qsTrId("#resultsHeadline")
|
||||
|
||||
var addition = qsTr("(Results)")
|
||||
|
||||
if(titleString !== undefined){
|
||||
if(titleString)
|
||||
return addition + " " + titleString
|
||||
}
|
||||
else {
|
||||
else
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function changeRoute(route) {
|
||||
|
@ -155,7 +156,7 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
selector.appear(selectOptions, qsTr("select cat"))
|
||||
selector.appear(selectOptions, qsTrId("#selectCategory"))
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import "../Components"
|
||||
|
||||
|
@ -28,32 +29,45 @@ DataListView {
|
|||
property bool ready
|
||||
|
||||
property string title: control.widgetData['comp_name']
|
||||
property string subTitle: qsTr("(Startlist)") + " " + control.widgetData['route_name'] //getSubtitle()
|
||||
property string subTitle: getSubtitle()
|
||||
property bool titleIsPageTitle: true
|
||||
|
||||
property Component headerComponent: ToolButton {
|
||||
property Component headerComponent: RowLayout {
|
||||
|
||||
height: parent.height
|
||||
spacing: 0
|
||||
|
||||
ToolButton {
|
||||
id: shareToolBt
|
||||
|
||||
onClicked: shareWidget(control.title)
|
||||
|
||||
text: "\uf1e0"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
id: moreToolBt
|
||||
|
||||
onClicked: {
|
||||
control.changeCat()
|
||||
}
|
||||
|
||||
icon.name: "menu"
|
||||
text: "\uf142"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
}
|
||||
|
||||
function getSubtitle() {
|
||||
var titleString
|
||||
var titleString = control.widgetData["route_name"]
|
||||
|
||||
for(var i = 0; i < control.widgetData["categorys"].length; i ++ ){
|
||||
//console.log("checking " + i + ": cat: " + parseInt(control.widgetData["categorys"][i]["GrpId"]) + " searched cat: " + params.cat)
|
||||
if(parseInt(control.widgetData["categorys"][i]["GrpId"]) === parseInt(params.cat)){
|
||||
titleString = control.widgetData["categorys"][i]["name"]
|
||||
}
|
||||
}
|
||||
|
||||
var addition = qsTr("(Startlist)")
|
||||
return addition + " " + titleString
|
||||
//% "(Startlist)"
|
||||
var addition = qsTrId("#startlistHeadline")
|
||||
|
||||
if(titleString)
|
||||
return addition + " " + titleString
|
||||
else
|
||||
return ""
|
||||
}
|
||||
|
||||
function changeRoute(route) {
|
||||
|
@ -77,7 +91,7 @@ DataListView {
|
|||
}
|
||||
}
|
||||
|
||||
selector.appear(selectOptions, qsTr("select cat"))
|
||||
selector.appear(selectOptions, qsTrId("#selectCategory"))
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
@ -116,7 +130,7 @@ DataListView {
|
|||
property int thisIndex: index
|
||||
property var thisData: widgetData[ "participants" ][index]
|
||||
|
||||
width: parent.width
|
||||
width: control.width
|
||||
height: 50
|
||||
|
||||
opacity: 0
|
||||
|
@ -138,15 +152,7 @@ DataListView {
|
|||
|
||||
text: ""
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
width: partDel.width
|
||||
|
||||
color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey"
|
||||
|
||||
opacity: 0.2
|
||||
}
|
||||
highlighted: partDel.thisIndex % 2 == 0
|
||||
|
||||
Row {
|
||||
id: partDelFirstRow
|
||||
|
|
|
@ -23,34 +23,102 @@ import QtQuick.Layouts 1.3
|
|||
import QtQuick.Controls.Material 2.12
|
||||
import QtPurchasing 1.12
|
||||
|
||||
import com.itsblue.digitalRockRanking 1.0
|
||||
import de.itsblue.blueROCK 1.0
|
||||
|
||||
import "./Pages"
|
||||
import "./Components"
|
||||
|
||||
import "./Widgets"
|
||||
|
||||
Window {
|
||||
visible: true
|
||||
width: 540
|
||||
height: 960
|
||||
title: qsTr("blueROCK")
|
||||
title: "blueROCK"
|
||||
|
||||
Page {
|
||||
id: app
|
||||
|
||||
property int errorCode: -1
|
||||
|
||||
property var colorShade: Material.theme === Material.Light ? Material.Shade300 : Material.Shade700
|
||||
property color nationalAdultsColor: Material.color(Material.Green, colorShade)
|
||||
property color nationalYouthColor: Material.color(Material.LightGreen, colorShade)
|
||||
property color federalColor: Material.color(Material.Grey, colorShade)
|
||||
|
||||
// comp cats source:
|
||||
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/dav_calendar.php
|
||||
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php
|
||||
|
||||
property var compCats: {
|
||||
// --- ICC ---
|
||||
|
||||
/*'int' : {
|
||||
'label' : 'International',
|
||||
'nation' : 'ICC',
|
||||
'wettk_reg' : '^[0-9]{2,2}[_^E]{1}[^YJ]{1,1}.*',
|
||||
'serie_reg' : '^[0-9]{2,2}_(WC|TR){1,1}.*',
|
||||
'rang_title': 'CUWR continuously updated WORLDRANKING',
|
||||
'bgcolor' : '#B8C8FF',
|
||||
'nat_team_ranking' : '',
|
||||
'cat_id' : [68,86]//[68,69,70,86,259]
|
||||
},*/
|
||||
'worldcup': {
|
||||
'label' : 'World Cups',
|
||||
'nation' : 'ICC',
|
||||
'bgcolor' : '#B8C8FF',
|
||||
'sort_rank': 1,
|
||||
'cat_id' : [69]
|
||||
},
|
||||
'youth' : {
|
||||
'label' : 'Youth Events',
|
||||
'nation' : 'ICC',
|
||||
'wettk_reg' : '^[0-9]{2,2}(EYC|_J|_Y){1,1}.*',
|
||||
'serie_reg' : '^[0-9]{2,2}_EYC',
|
||||
'rang_title': '',
|
||||
'bgcolor' : '#D8E8FF',
|
||||
'sort_rank': 2,
|
||||
'cat_id' : [71,258]
|
||||
},
|
||||
'cont': {
|
||||
'label' : 'Continental Events',
|
||||
'nation' : 'ICC',
|
||||
'bgcolor' : '#B8C8FF',
|
||||
'sort_rank': 3,
|
||||
'cat_id' : [262]
|
||||
},
|
||||
'masters' : {
|
||||
'label' : 'Masters and Promo Events',
|
||||
'nation' : 'ICC',
|
||||
'wettk_reg' : '^[0-9]{2,2}_[^PWERASL]{1}.*',
|
||||
// 'serie_reg' : '^[0-9]{2,2}_(WC|TR){1,1}.*',
|
||||
// 'rang_title': 'CUWR continuously updated WORLDRANKING',
|
||||
'bgcolor' : '#F0F0F0',
|
||||
'sort_rank': 4,
|
||||
'cat_id' : [70]
|
||||
},
|
||||
'para' : {
|
||||
'label' : 'Paraclimbing Events',
|
||||
'nation' : 'ICC',
|
||||
'wettk_reg' : '^[0-9]{2,2}_PE.*',
|
||||
'bgcolor' : '#F0F0F0',
|
||||
'sort_rank': 5,
|
||||
'cat_id' : [256,259]
|
||||
},
|
||||
'games': {
|
||||
'label': 'Games',
|
||||
'nation': 'ICC',
|
||||
'bgcolor' : '#B8C8FF',
|
||||
'sort_rank': 6,
|
||||
'cat_id': [68,86]
|
||||
},
|
||||
|
||||
// --- GER ---
|
||||
|
||||
'ger_meisterschaft' : {
|
||||
'label' : 'Deutsche Meisterschaft',
|
||||
'nation' : 'GER',
|
||||
'bgcolor' : '#A8F0A8',
|
||||
'bgcolor' : app.nationalAdultsColor,//'#A8F0A8',
|
||||
'sort_rank': 1,
|
||||
'cat_id' : [57, 59, 60]
|
||||
},
|
||||
|
@ -61,7 +129,7 @@ Window {
|
|||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WL]+.*',
|
||||
'serie_reg' : '^[0-9]{2,2}_JC',
|
||||
// 'rang_title': 'Deutsche Jugend RANGLISTE',
|
||||
'bgcolor' : '#D8FFD8',
|
||||
'bgcolor' : app.nationalYouthColor,//'#D8FFD8',
|
||||
'sort_rank': 2,
|
||||
'cat_id' : [58]
|
||||
},
|
||||
|
@ -71,7 +139,7 @@ Window {
|
|||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
||||
'serie_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
||||
'rang_title': '',
|
||||
'bgcolor' : '#F0F0F0',
|
||||
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||
'sort_rank': 3,
|
||||
'cat_id' : [61,56]
|
||||
},
|
||||
|
@ -84,7 +152,7 @@ Window {
|
|||
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
||||
'serie_reg' : '.*',
|
||||
'rang_title': 'SWISS RANKING',
|
||||
'bgcolor' : '#A8F0A8',
|
||||
'bgcolor' : app.nationalAdultsColor, //'#A8F0A8',
|
||||
'sort_rank': 1,
|
||||
'cat_id' : [62,63]
|
||||
},
|
||||
|
@ -94,7 +162,7 @@ Window {
|
|||
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
||||
'serie_reg' : '.*',
|
||||
'rang_title': 'SWISS RANKING',
|
||||
'bgcolor' : '#D8FFD8',
|
||||
'bgcolor' : app.nationalYouthColor, //'#D8FFD8',
|
||||
'sort_rank': 2,
|
||||
'cat_id' : [65]
|
||||
},
|
||||
|
@ -103,16 +171,16 @@ Window {
|
|||
'nation' : 'SUI',
|
||||
'wettk_reg' : '^[0-9]{2,2}_RG_.*',
|
||||
'rang_title': '',
|
||||
'bgcolor' : '#F0F0F0',
|
||||
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||
'sort_rank': 3,
|
||||
'cat_id' : [64]
|
||||
},
|
||||
'sui_ice' : {
|
||||
'label' : 'Iceclimbing',
|
||||
'nation' : 'SUI',
|
||||
'wettk_reg' : '^[0-9]{2,2}_RC_.*',
|
||||
'wparams["valid"]ettk_reg' : '^[0-9]{2,2}_RC_.*',
|
||||
'rang_title': '',
|
||||
'bgcolor' : '#F0F0F0',
|
||||
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||
'sort_rank': 4,
|
||||
'cat_id' : [84]
|
||||
}
|
||||
|
@ -120,7 +188,7 @@ Window {
|
|||
|
||||
anchors.fill: parent
|
||||
|
||||
//Material.theme: Material.Dark
|
||||
Material.theme: appSettings.read("darkTheme") === "true" ? Material.Dark:Material.Light
|
||||
|
||||
Component.onCompleted: {
|
||||
//loadingDl.open()
|
||||
|
@ -129,6 +197,18 @@ Window {
|
|||
//mainStack.push("Pages/AthleteSearchPage.qml")
|
||||
//openWidget({comp: 11651, cat: 26})
|
||||
//openWidget({person: 6623})
|
||||
//console.log(JSON.stringify(serverConn.getParamsFromUrl("")))
|
||||
//openWidgetFromUrl("https://l.bluerock.dev/?comp=11501&cat=GER_M")
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: fa5solid
|
||||
source: "qrc:/fonts/fa5solid.otf"
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: fa5regular
|
||||
source: "qrc:/fonts/fa5regular.otf"
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
|
@ -137,8 +217,12 @@ Window {
|
|||
onActivated: app.goBack()
|
||||
}
|
||||
|
||||
ServerConn {
|
||||
BlueRockBackend {
|
||||
id: serverConn
|
||||
|
||||
onOpenedViaUrl: {
|
||||
app.openWidgetFromUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
AppSettings {
|
||||
|
@ -225,7 +309,9 @@ Window {
|
|||
height: parent.height
|
||||
|
||||
onClicked: app.goBack()
|
||||
icon.name: "back"
|
||||
|
||||
text: "\uf053"
|
||||
font.family: fa5solid.name
|
||||
}
|
||||
|
||||
Column {
|
||||
|
@ -235,20 +321,16 @@ Window {
|
|||
height: childrenRect.height
|
||||
width: parent.width - extraComponentLoader.width - toolButton.width - 3 * parent.spacing
|
||||
|
||||
Label {
|
||||
MovingLabel {
|
||||
id: toolBarTitleLa
|
||||
|
||||
syncWithLabel: toolBarSubTitleLa
|
||||
width: parent.width
|
||||
|
||||
scale: 1
|
||||
|
||||
elide: "ElideRight"
|
||||
|
||||
font.bold: true
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
color: "black"
|
||||
|
||||
text: getText()
|
||||
|
||||
function getText(){
|
||||
|
@ -277,18 +359,16 @@ Window {
|
|||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
MovingLabel {
|
||||
id: toolBarSubTitleLa
|
||||
|
||||
width: parent.width
|
||||
height: text !== "" ? undefined:0
|
||||
visible: text !== ""
|
||||
|
||||
elide: "ElideRight"
|
||||
syncWithLabel: toolBarTitleLa
|
||||
width: parent.width
|
||||
|
||||
font.bold: false
|
||||
|
||||
color: "black"
|
||||
|
||||
text: getText()
|
||||
|
||||
function getText(){
|
||||
|
@ -298,7 +378,7 @@ Window {
|
|||
titleString = mainStack.currentItem.subTitle
|
||||
}
|
||||
|
||||
return(titleString)
|
||||
return titleString
|
||||
}
|
||||
|
||||
Behavior on text {
|
||||
|
@ -466,7 +546,8 @@ Window {
|
|||
font.bold: true
|
||||
color: "white"
|
||||
|
||||
text: "loading..."
|
||||
//% "Loading"
|
||||
text: qsTrId("#loading") + "..."
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,6 +588,13 @@ Window {
|
|||
return app.height < app.width
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
var dark = app.Material.theme === Material.Light
|
||||
|
||||
app.Material.theme = dark ? Material.Dark : Material.Light
|
||||
appSettings.write("darkTheme", dark)
|
||||
}
|
||||
|
||||
function largeScreen() {
|
||||
return Math.min(app.width, app.height) > 750
|
||||
}
|
||||
|
@ -514,17 +602,36 @@ Window {
|
|||
function openWidget(params) {
|
||||
loadingDl.open()
|
||||
|
||||
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
||||
app.errorCode = calComp.status
|
||||
console.log("Opening widget: ", JSON.stringify(params))
|
||||
|
||||
if(calComp.ready) {
|
||||
mainStack.push(calComp)
|
||||
}
|
||||
else {
|
||||
delete(calComp)
|
||||
var result = false
|
||||
|
||||
if(Object.keys(params).length) {
|
||||
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
||||
app.errorCode = calComp.status
|
||||
|
||||
if(calComp.ready) {
|
||||
mainStack.push(calComp)
|
||||
result = true
|
||||
}
|
||||
else {
|
||||
delete(calComp)
|
||||
}
|
||||
}
|
||||
|
||||
loadingDl.close()
|
||||
return result
|
||||
}
|
||||
|
||||
function openWidgetFromUrl(url) {
|
||||
var result = serverConn.getParamsFromUrl(url)
|
||||
|
||||
if(result["valid"]) {
|
||||
openWidget(result["params"])
|
||||
return app.errorCode !== 906
|
||||
}
|
||||
|
||||
return result["valid"]
|
||||
}
|
||||
|
||||
function defaultString(string, defaultString) {
|
||||
|
@ -549,66 +656,36 @@ Window {
|
|||
// 2 - error
|
||||
|
||||
var errorString
|
||||
var errorDescription
|
||||
|
||||
switch(errorCode) {
|
||||
case 0:
|
||||
infoLevel = 2
|
||||
errorString = "No connection to server"
|
||||
errorDescription = "Please check your internet connection and try again."
|
||||
//% "No connection to server"
|
||||
errorString = qsTrId("#noConnectionError")
|
||||
break
|
||||
case 200:
|
||||
infoLevel = 0
|
||||
errorString = "Success"
|
||||
errorDescription = "The request was successfull"
|
||||
break
|
||||
case 401:
|
||||
case 404:
|
||||
infoLevel = 2
|
||||
errorString = "Authentication required"
|
||||
errorDescription = "The server asked for user credentinals, please chack them and try again"
|
||||
break
|
||||
case 500:
|
||||
infoLevel = 2
|
||||
errorString = "Internal server error"
|
||||
errorDescription = "The server was unable to process this request, this is probaply the servers fault. Please try again later."
|
||||
break
|
||||
case 900:
|
||||
infoLevel = 2
|
||||
errorString = "Internal error"
|
||||
errorDescription = "Something went wron internally, this is probaply an inssue in the program code"
|
||||
//% "Not found"
|
||||
errorString = qsTrId("#notFoundError")
|
||||
break
|
||||
case 901:
|
||||
infoLevel = 1
|
||||
errorString = "No Data"
|
||||
errorDescription = "There is currently no data available. Please try again later."
|
||||
//% "No Data"
|
||||
errorString = qsTrId("#noDataError")
|
||||
break
|
||||
case 902:
|
||||
infoLevel = 1
|
||||
errorString = "Cached (old) data"
|
||||
errorDescription = "Es konnte keine Verbindung zum Server hergestellt werden, aber es sind noch alte Daten gespeichert."
|
||||
break
|
||||
case 903:
|
||||
infoLevel = 1
|
||||
errorString = "Ungültiger Aufruf"
|
||||
errorDescription = "Die aufgerufene Funktion ist momentan nicht verfügbar, bitte versuche es später erneut."
|
||||
break
|
||||
case 904:
|
||||
case 906:
|
||||
infoLevel = 2
|
||||
errorString = "Incompatible API"
|
||||
errorDescription = "Please make shure that you are using the latest version of this app and try again."
|
||||
break
|
||||
case 905:
|
||||
infoLevel = 1
|
||||
errorString = "Loading..."
|
||||
errorDescription = "Please wait while we're loading some data"
|
||||
//% "Invalid Request"
|
||||
errorString = qsTrId("#invalidRequestError")
|
||||
errorDescription = "Invalid Request"
|
||||
break
|
||||
default:
|
||||
infoLevel = 2
|
||||
errorString = "Unexpected error ("+errorCode+")"
|
||||
errorDescription = "Unexpected error while getting data from the server. Please try again later."
|
||||
//% "Unexpected error"
|
||||
errorString = qsTrId("#unexpectedError") + " ("+errorCode+")"
|
||||
}
|
||||
|
||||
return([infoLevel, errorString, errorDescription])
|
||||
return([infoLevel, errorString])
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,5 +26,10 @@
|
|||
<file>Components/SpeedFlowChartPopup.qml</file>
|
||||
<file>Components/BlueRockBadge.qml</file>
|
||||
<file>Components/DisclaimerDialog.qml</file>
|
||||
<file>Components/ColoredItemDelegate.qml</file>
|
||||
<file>Components/AlignedButton.qml</file>
|
||||
<file>Components/SharePopup.qml</file>
|
||||
<file>Pages/QrCodeScanPage.qml</file>
|
||||
<file>Components/MovingLabel.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
BIN
resources/shared/PosterTemplate.png
Normal file
After Width: | Height: | Size: 235 KiB |
BIN
resources/shared/PosterTemplate.xcf
Normal file
BIN
resources/shared/fonts/OpenSans-Light.ttf
Normal file
BIN
resources/shared/fonts/fa5regular.otf
Normal file
BIN
resources/shared/fonts/fa5solid.otf
Normal file
Before Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 369 B |
Before Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 501 B |
Before Width: | Height: | Size: 843 B |
Before Width: | Height: | Size: 731 B |
Before Width: | Height: | Size: 363 B |
Before Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 672 B |
Before Width: | Height: | Size: 778 B |
Before Width: | Height: | Size: 126 B |
Before Width: | Height: | Size: 545 B |
Before Width: | Height: | Size: 663 B |
Before Width: | Height: | Size: 158 B |
Before Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 576 B |
Before Width: | Height: | Size: 960 B |
Before Width: | Height: | Size: 821 B |
Before Width: | Height: | Size: 591 B |
Before Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 626 B |
Before Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 975 B |
Before Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 130 B |
Before Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 884 B |
Before Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 674 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 957 B |
Before Width: | Height: | Size: 727 B |
Before Width: | Height: | Size: 452 B |