diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2fae8..d33bcbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,5 +51,10 @@ * Account deletion now also removes news, image data and events from local db # Translations # -* Italian thanks to Davide de Prisco +* Italian thanks to Davide de Prisco +## v0.2.1 ## +* Fix for [issue 4](https://github.com/LubuWest/Friendiqa/issues/4) +* Fix for Friendica [issue 4689](https://github.com/friendica/friendica/issues/4689) +* Long posts are automatically truncated +* Intents for pictures (Send one image from gallery: attach to message, send multiple images: upload to album) diff --git a/Friendiqa_v0.2.1.apk b/Friendiqa_v0.2.1.apk new file mode 100644 index 0000000..12c4931 Binary files /dev/null and b/Friendiqa_v0.2.1.apk differ diff --git a/README.md b/README.md index 7cb6243..6e062f8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ QML based client for the Friendica Social Network. Tabs for news (incl. Direct Messages), friends, photos and events. - OS: currently Linux and Android(4.3). + OS: currently Linux and Android (4.3 Jelly Bean). Source code is a QtCreator project. ## Screenshots ## @@ -20,15 +20,16 @@ QML based client for the Friendica Social Network. Currently supported: * Shows Posts from friends, favorited messages, Direct Messages and Notifications * Open links in external browser -* -Click on contact photo for contact details +* Click on contact photo for contact details * Click on like text for additional contact info * Deletion, Reposting, Answering of Posts +* Expand truncated news items * Liking, disliking, favoriting * Attending for event posts * Update fetches new posts (up to last 50) since last in local DB * More shows older posts from local DB * Create new Message with images or direct messages, Contact/Group access rights(can be stored), smileys +* Send image from Android gallery * Native Android image dialog ToDo: @@ -64,7 +65,7 @@ ToDo: # Images # Currently supported: * Download public and private own images to local directory -* Upload picture to album with descriptions(public) +* Upload picture to album with descriptions(public), send from gallery * Delete own pictures and albums on client and server * Show albums in grid, show images in album in grid and fullscreen * Show public and private (Friendica 3.6 server required) albums and images of contacts diff --git a/source-android/android/AndroidManifest.xml b/source-android/android/AndroidManifest.xml index 87adccf..841fac6 100644 --- a/source-android/android/AndroidManifest.xml +++ b/source-android/android/AndroidManifest.xml @@ -1,11 +1,17 @@ - + - + + + + + + + diff --git a/source-android/android/libcrypto.so b/source-android/android/libcrypto.so old mode 100755 new mode 100644 diff --git a/source-android/android/libssl.so b/source-android/android/libssl.so old mode 100755 new mode 100644 diff --git a/source-android/android/src/FriendiqaActivity.java b/source-android/android/src/FriendiqaActivity.java index 2b04ff3..ce892f3 100644 --- a/source-android/android/src/FriendiqaActivity.java +++ b/source-android/android/src/FriendiqaActivity.java @@ -2,9 +2,9 @@ package androidnative.friendiqa; import androidnative.AndroidNativeActivity; -/** - * Created by benlau on 8/3/2017. - */ + + + public class FriendiqaActivity extends AndroidNativeActivity { public FriendiqaActivity() { @@ -13,4 +13,8 @@ public class FriendiqaActivity extends AndroidNativeActivity { QT_ANDROID_THEMES = new String[] {""}; QT_ANDROID_DEFAULT_THEME = ""; } + + + + } diff --git a/source-android/android/src/QSharePathResolver.java b/source-android/android/src/QSharePathResolver.java new file mode 100644 index 0000000..3f52896 --- /dev/null +++ b/source-android/android/src/QSharePathResolver.java @@ -0,0 +1,205 @@ +// 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; + +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)) { + 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)) { + + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } + // MediaProvider + else if (isMediaDocument(uri)) { + 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())) { + + // Return the remote address + if (isGooglePhotosUri(uri)) + return uri.getLastPathSegment(); + + return getDataColumn(context, uri, null, null); + } + // Other Providers + else{ + 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())) { + + // Return the remote address + if (isGooglePhotosUri(uri)) + return uri.getLastPathSegment(); + + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + 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()); + } + +} diff --git a/source-android/androidnative.pri/ci/qt-android b/source-android/androidnative.pri/ci/qt-android old mode 100755 new mode 100644 diff --git a/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.cpp b/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.cpp index bb0695f..5f4a241 100644 --- a/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.cpp +++ b/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.cpp @@ -23,3 +23,8 @@ void AndroidNative::SystemDispatcherProxy::loadClass(QString className) { SystemDispatcher::instance()->loadClass(className); } + +void AndroidNative::SystemDispatcherProxy::setInitialized() +{ + SystemDispatcher::instance()->setInitialized(); +} diff --git a/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.h b/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.h index 4fb07ad..a24c870 100644 --- a/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.h +++ b/source-android/androidnative.pri/cpp/AndroidNative/priv/systemdispatcherproxy.h @@ -16,6 +16,8 @@ namespace AndroidNative { Q_INVOKABLE void loadClass(QString className); + Q_INVOKABLE void setInitialized(); + signals: void dispatched(QString type , QVariantMap message); diff --git a/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.cpp b/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.cpp index 008b154..6b3fd86 100644 --- a/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.cpp +++ b/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.cpp @@ -332,6 +332,16 @@ void AndroidNative::SystemDispatcher::loadClass(QString javaClassName) dispatch("androidnative.SystemDispatcher.loadClass",message); } + +void AndroidNative::SystemDispatcher::setInitialized() +{ + QVariantMap message; + message["Initialized"] = true; + + dispatch("androidnative.SystemDispatcher.setInitialized",message); +} + + void AndroidNative::SystemDispatcher::registerNatives() { Q_UNUSED(registerNativesCalled); diff --git a/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.h b/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.h index a4e1a4c..007045f 100644 --- a/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.h +++ b/source-android/androidnative.pri/cpp/AndroidNative/systemdispatcher.h @@ -29,6 +29,9 @@ namespace AndroidNative { */ Q_INVOKABLE void loadClass(QString javaClassName); + //inform Dispatcher about loaded Environment, Intents can be processed + Q_INVOKABLE void setInitialized(); + /// Register JNI native methods. This function must be called in JNI_OnLoad. Otherwise, the messenger will not be working static void registerNatives(); diff --git a/source-android/androidnative.pri/examples/androidnativeexample/android-sources/gradlew b/source-android/androidnative.pri/examples/androidnativeexample/android-sources/gradlew old mode 100755 new mode 100644 diff --git a/source-android/androidnative.pri/examples/androidnativeexample/res/drawable-xxhdpi/ic_menu.png b/source-android/androidnative.pri/examples/androidnativeexample/res/drawable-xxhdpi/ic_menu.png old mode 100755 new mode 100644 diff --git a/source-android/androidnative.pri/java/src/androidnative/AndroidNativeActivity.java b/source-android/androidnative.pri/java/src/androidnative/AndroidNativeActivity.java index c63c3b8..08ac034 100644 --- a/source-android/androidnative.pri/java/src/androidnative/AndroidNativeActivity.java +++ b/source-android/androidnative.pri/java/src/androidnative/AndroidNativeActivity.java @@ -1,5 +1,9 @@ package androidnative; import android.content.Intent; +import android.util.Log; +import android.app.Activity; +import android.os.*; +import java.util.Map; /** An alternative Activity class for Qt applicaiton. @@ -14,10 +18,38 @@ public class AndroidNativeActivity extends org.qtproject.qt5.android.bindings.Qt SystemDispatcher.onActivityResult(requestCode,resultCode,data); } + protected void onResume() { super.onResume(); - SystemDispatcher.onActivityResume(); + + if((getIntent().getFlags() == (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)) || (getIntent().getFlags() == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) || (getIntent().getFlags() == Intent.FLAG_ACTIVITY_NEW_TASK) || (getIntent().getFlags() == Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)) { + SystemDispatcher.onActivityResume(); + } else { + Intent data = getIntent(); + if ((data != null) && !(data.getBooleanExtra("used",false))){ + SystemDispatcher.loadClass("androidnative.ImagePicker"); + SystemDispatcher.onActivityResult(0x245285a3,Activity.RESULT_OK,data); + getIntent().replaceExtras(new Bundle()); + getIntent().setAction(""); + getIntent().setData(null); + getIntent().setFlags(0); + getIntent().putExtra("used", true); + + } else { + SystemDispatcher.onActivityResume(); + }} } + protected void onNewIntent(Intent data) { + super.onNewIntent(data); + SystemDispatcher.loadClass("androidnative.ImagePicker"); + SystemDispatcher.onActivityResult(0x245285a3,Activity.RESULT_OK,data); + getIntent().replaceExtras(new Bundle()); + getIntent().setAction(""); + getIntent().setData(null); + getIntent().setFlags(0); + getIntent().putExtra("used", true); + + } // onNewIntent } diff --git a/source-android/androidnative.pri/java/src/androidnative/SystemDispatcher.java b/source-android/androidnative.pri/java/src/androidnative/SystemDispatcher.java index b35dff3..3ba45e5 100644 --- a/source-android/androidnative.pri/java/src/androidnative/SystemDispatcher.java +++ b/source-android/androidnative.pri/java/src/androidnative/SystemDispatcher.java @@ -17,6 +17,9 @@ import android.util.Log; import android.os.Handler; import android.os.Looper; import android.content.Intent; +//import android.content.*; +//import android.app.*; +import android.os.*; import java.util.concurrent.Semaphore; import java.io.StringWriter; import java.io.PrintWriter; @@ -110,6 +113,14 @@ public class SystemDispatcher { public static String SYSTEM_DISPATCHER_LOAD_CLASS_MESSAGE = "androidnative.SystemDispatcher.loadClass"; + public static String SYSTEM_DISPATCHER_SET_INITIALIZED_MESSAGE = "androidnative.SystemDispatcher.setInitialized"; + + public static boolean isIntentPending=false; + + public static boolean isInitialized=false; + + private static Map waitingIntent; + /** A helper function to dispatch a massage when onResume is invoked in the Activity class */ @@ -117,6 +128,7 @@ public class SystemDispatcher { dispatch(ACTIVITY_RESUME_MESSAGE); } + /** A helper function to dispatch a message based on the input argument fron Activity.onActivityResult */ public static void onActivityResult (int requestCode, int resultCode, Intent data) { @@ -126,7 +138,17 @@ public class SystemDispatcher { message.put("resultCode",resultCode); message.put("data",data); + + if(isInitialized) { dispatch(ACTIVITY_RESULT_MESSAGE,message); + waitingIntent=null; + isIntentPending=false; + } else { //onIntent start + waitingIntent = message; + isIntentPending = true; + } + //onIntent end + } private static class Payload { @@ -201,6 +223,18 @@ public class SystemDispatcher { } } + + + public static void setInitialized() { + isInitialized = true; + if(isIntentPending) { + isIntentPending = false; + dispatch(ACTIVITY_RESULT_MESSAGE,waitingIntent); + } +} + + + public static void init() { SystemDispatcher.addListener(new SystemDispatcher.Listener() { public void onDispatched(String type , Map message) { @@ -210,6 +244,9 @@ public class SystemDispatcher { String className = (String) message.get("className"); loadClass(className); } + if (type.equals(SYSTEM_DISPATCHER_SET_INITIALIZED_MESSAGE)) { + setInitialized(); + } } }); diff --git a/source-android/androidnative.pri/tests/instrument/activity/android-sources/gradlew b/source-android/androidnative.pri/tests/instrument/activity/android-sources/gradlew old mode 100755 new mode 100644 diff --git a/source-android/androidnative.pri/tests/instrument/runner/gradlew b/source-android/androidnative.pri/tests/instrument/runner/gradlew old mode 100755 new mode 100644 diff --git a/source-android/js/helper.js b/source-android/js/helper.js index a8f2af8..d949a81 100644 --- a/source-android/js/helper.js +++ b/source-android/js/helper.js @@ -40,11 +40,11 @@ function friendicaRequest(login,api,rootwindow,callback) { if (xhrequest.status=200){ callback(xhrequest.responseText) }else{ - showMessage("Error","API:" +login.server+api+"\n NO RESPONSE"+xhrequest.statusText,rootwindow); + showMessage("Error","API:\n" +login.server+api+"\n NO RESPONSE"+xhrequest.statusText,rootwindow); } } catch (e){ - showMessage("Error", login.server+api+"\n"+e+"\n Return: "+xhrequest.responseText,rootwindow) + showMessage("Error", "API:\n" +login.server+api+"\n"+e+"\n Return: "+xhrequest.responseText,rootwindow) } } } @@ -61,11 +61,11 @@ function friendicaPostRequest(login,api,data,method,rootwindow,callback) { try{ if (xhrequest.responseText!=""){ callback(xhrequest.responseText) }else{ - showMessage("Error",api+" NO RESPONSE",rootwindow) + showMessage("Error","API:\n" +api+" NO RESPONSE",rootwindow) callback(xhrequest.responseText) } } - catch (e){showMessage("Error", api+" "+e+"\n Return:"+xhrequest.responseText,rootwindow)} + catch (e){showMessage("Error", "API:\n" + +api+" "+e+"\n Return:"+xhrequest.responseText,rootwindow)} } } xhrequest.open(method, login.server+api,true,login.username,Qt.atob(login.password)); @@ -88,7 +88,7 @@ function friendicaWebRequest(url,rootwindow,callback) { if (xhrequest.readyState === XMLHttpRequest.HEADERS_RECEIVED) {} else if(xhrequest.readyState === XMLHttpRequest.DONE) { try{callback(xhrequest.responseText)} - catch (e){showMessage("Error",url+" "+e+"\n Return: "+xhrequest.responseText, rootwindow)} + catch (e){showMessage("Error","API:\n" +url+" "+e+"\n Return: "+xhrequest.responseText, rootwindow)} } } xhrequest.open("GET", url,true); @@ -101,7 +101,7 @@ function friendicaRemoteAuthRequest(login,url,c_url,rootwindow,callback) { if (xhrequest.readyState === XMLHttpRequest.HEADERS_RECEIVED) {} else if(xhrequest.readyState === XMLHttpRequest.DONE) { try{callback(xhrequest.responseText)} - catch (e){showMessage("Error",url+" "+e+"\n Return: "+xhrequest.responseText, rootwindow)} + catch (e){showMessage("Error","Url:\n" +url+" "+e+"\n Return: "+xhrequest.responseText, rootwindow)} } } xhrequest.open("GET", login.server+"/api/friendica/remoteauth?c_url="+c_url+"&url="+url,true,login.username,Qt.atob(login.password)); diff --git a/source-android/js/news.js b/source-android/js/news.js index c058c50..9e53bfe 100644 --- a/source-android/js/news.js +++ b/source-android/js/news.js @@ -38,7 +38,7 @@ function requestFriends(login,database,rootwindow,callback){ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]); db.transaction( function(tx) { var result = tx.executeSql('UPDATE contacts SET isFriend=0 where username="'+login.username+'"')}); // clean old friends - Helperjs.friendicaRequest(login,"/api/statuses/friends", rootwindow,function (obj){ + Helperjs.friendicaRequest(login,"/api/statuses/friends?count=9999", rootwindow,function (obj){ var friends=JSON.parse(obj); for (var i=0;i0){ var oldestnewsTime=oldestnewsrs.rows.item(0).created_at- 604800000;} else{var oldestnewsTime=0} //contacts can be 7 days old //print(login.username+" älteste news: "+ oldestnewsTime); var result = tx.executeSql('SELECT * from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge<'+oldestnewsTime); // check for friends for (var i=0;i0){attachImage(attachImageURLs[0])} } diff --git a/source-android/qml/newsqml/NewsTab.qml b/source-android/qml/newsqml/NewsTab.qml index 4b35b70..5db9a5c 100644 --- a/source-android/qml/newsqml/NewsTab.qml +++ b/source-android/qml/newsqml/NewsTab.qml @@ -36,6 +36,8 @@ import "qrc:/js/news.js" as Newsjs import "qrc:/js/helper.js" as Helperjs import "qrc:/js/service.js" as Service +//import AndroidNative 1.0 + Item { Connections{ target:newstab @@ -44,18 +46,6 @@ Item { } } -// Connections{ -// target:root -// onCurrentContactChanged:{ -// if (root.newContacts.length>0){ -// if(root.currentContact0){showNews(root.news)} - else{ newstab.newstabstatus=login.newsViewType; - if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} - else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + + //print("imageUrls "+JSON.stringify(imageUrls)+" newsstack.depth:"+newsStack.depth); + //newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{attachImageURLs:[imageUrl]}}) +// var imagePicker = Qt.createQmlObject('import QtQuick 2.0; import "qrc:/qml/genericqml";'+ +// osSettings.imagePickQml+'{multiple : true; onReady: {'+ +// 'if(imageUrls.length==1){root.currentIndex=0;newstab.active=true;root.uploadSignal(imageUrls)} else{'+ +// ' root.currentIndex=2;fotostab.active=true;'+ +// 'root.uploadSignal(imageUrls)};}}',newstab,"imagePicker"); + //SystemDispatcher.setInitialized(); + if(root.news.length>0){showNews(root.news)} + else{ newstab.newstabstatus=login.newsViewType; + if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + } } - }} + } } } diff --git a/source-android/qml/newsqml/Newsitem.qml b/source-android/qml/newsqml/Newsitem.qml index c046235..77f59fa 100644 --- a/source-android/qml/newsqml/Newsitem.qml +++ b/source-android/qml/newsqml/Newsitem.qml @@ -34,15 +34,15 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import "qrc:/js/news.js" as Newsjs import "qrc:/js/helper.js" as Helperjs - +import "qrc:/qml/genericqml" Item { id: newsitem width: parent.width height:toprow.height+friendicaActivities.height+controlrow.height+1//Math.max((itemMessage.height+topFlow.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm) + property int itemindex: index property string attending: "" - property int itemindex: index onAttendingChanged: {attendLabel.visible=true; attendLabel.text= qsTr("attending: ")+ qsTr(attending)} @@ -145,8 +145,9 @@ Item { textFormat: Text.RichText text:Qt.atob(newsitemobject.statusnet_html) width: newsitem.width-8*mm-2 - height: implicitHeight + height: Math.min(implicitHeight,3/4*root.height) wrapMode: Text.Wrap + clip:true onLinkActivated:{ Qt.openUrlExternally(link)} Component.onCompleted:{ @@ -159,6 +160,29 @@ Item { } } } + BlueButton{ + width: newsitem.width-8*mm-2 + height:10*mm + anchors.bottom: itemMessage.bottom + visible: itemMessage.implicitHeight>3/4*root.height + text:"\uf078" + fontColor:"grey" + border.color: "transparent" + gradient: Gradient { + GradientStop { position: 0.0; color: "transparent" } + GradientStop { position: 0.5; color: "white" } + } + radius:0 + onClicked: { + if (text=="\uf078"){ + itemMessage.height=itemMessage.implicitHeight+10*mm;text="\uf077" + } else { + itemMessage.height=Math.min(itemMessage.implicitHeight,3/4*root.height); + text="\uf078"; + newsView.positionViewAtIndex(index,ListView.Beginning); + } + } + } } } } diff --git a/source-android/qml/photoqml/ImageUploadDialog.qml b/source-android/qml/photoqml/ImageUploadDialog.qml index 23b37c6..ca379f0 100644 --- a/source-android/qml/photoqml/ImageUploadDialog.qml +++ b/source-android/qml/photoqml/ImageUploadDialog.qml @@ -64,12 +64,10 @@ Rectangle{ imageUploadModel.append({"imageUrl":url,"description":""}) } - z:2 - border.color: "grey" - width: parent.width-4*mm - height:parent.height-12*mm - x:2*mm - y:10*mm + //border.color: "grey" + y:1 + width:root.width-mm + height:root.height-5*mm property string directory: "" Connections{ @@ -95,8 +93,12 @@ Rectangle{ anchors.topMargin: 1*mm anchors.right: parent.right anchors.rightMargin: 1*mm - spacing:mm - + spacing:5*mm + Text{ + font.pixelSize: 3.5*mm + font.bold: true + text:qsTr("Upload to album") + } // BlueButton{ // id:permButton // text: ((contact_allow.length==0)&&(contact_deny.length==0)&&(group_allow.length==0)&&(group_deny.length==0))?"\uf09c":"\uf023" @@ -109,7 +111,9 @@ Rectangle{ BlueButton{ id:closeButton text: "\uf057" - onClicked:{imageDialog.destroy()} + onClicked:{photoStack.pop(); + //imageDialog.destroy() + } } } @@ -250,6 +254,9 @@ Rectangle{ albumModel.append({"text":storedAlbums[n]})} })} catch (e){print(e)} + if(attachImageURLs.length>0){ + for (var n in attachImageURLs){attachImage(attachImageURLs[n])} + } } // BusyIndicator{ // id: imageBusy diff --git a/source-android/qml/photoqml/PhotoTab.qml b/source-android/qml/photoqml/PhotoTab.qml index c42b23e..de6e1da 100644 --- a/source-android/qml/photoqml/PhotoTab.qml +++ b/source-android/qml/photoqml/PhotoTab.qml @@ -37,7 +37,11 @@ import "qrc:/js/helper.js" as Helperjs import "qrc:/qml/photoqml" import "qrc:/qml/genericqml" -Rectangle { + +StackView{ + id: photoStack + anchors.fill:parent + initialItem:Rectangle { id:fotorectangle y:1 width:root.width-mm @@ -48,15 +52,15 @@ Rectangle { property bool remoteContact: false onNewimagesChanged:{ - if(newimages.length>0){ + if(fotorectangle.newimages.length>0){ //print("newimages "+JSON.stringify(newimages)); var ownimagelist=[]; Helperjs.readField("album",root.db,"imageData",root.login.username,function(albums){ - for (var i=0;i0){ var oldestnewsTime=oldestnewsrs.rows.item(0).created_at- 604800000;} else{var oldestnewsTime=0} //contacts can be 7 days old //print(login.username+" älteste news: "+ oldestnewsTime); var result = tx.executeSql('SELECT * from contacts WHERE username="'+login.username+'" AND isFriend=0 AND imageAge<'+oldestnewsTime); // check for friends for (var i=0;i0){attachImage(attachImageURLs[0])} } diff --git a/source-linux/qml/newsqml/NewsTab.qml b/source-linux/qml/newsqml/NewsTab.qml index 4b35b70..07be7d7 100644 --- a/source-linux/qml/newsqml/NewsTab.qml +++ b/source-linux/qml/newsqml/NewsTab.qml @@ -36,6 +36,8 @@ import "qrc:/js/news.js" as Newsjs import "qrc:/js/helper.js" as Helperjs import "qrc:/js/service.js" as Service +//import AndroidNative 1.0 + Item { Connections{ target:newstab @@ -44,18 +46,6 @@ Item { } } -// Connections{ -// target:root -// onCurrentContactChanged:{ -// if (root.newContacts.length>0){ -// if(root.currentContact1){newsStack.pop()}}catch(e){} - newsBusy.running=false; + newsBusy.running = false; var currentTime= new Date(); downloadNotice.text=""; var msg = {'currentTime': currentTime, 'model': newsModel,'news':newsToShow}; @@ -123,7 +115,11 @@ Item { newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{"reply_to_user": friend,"directmessage":1,"login":root.login}}); } - + function sendUrls(urls){ + if((urls.length==1)&&(newsStack.depth<2)){ + newsStack.push([newslistRectangle,{item:"qrc:/qml/newsqml/MessageSend.qml",properties:{attachImageURLs:urls}}]) + } + } StackView{ id: newsStack @@ -131,6 +127,7 @@ Item { property int conversationIndex: 0 initialItem:Rectangle { + id:newslistRectangle y:1 color: "white" @@ -340,12 +337,23 @@ Item { root.messageSignal.connect(onFriendsMessages); root.directmessageSignal.connect(onDirectMessage); root.newsSignal.connect(showNews); + root.uploadSignal.connect(sendUrls); try{newsModel.clear()} catch(e){} - if(root.news.length>0){showNews(root.news)} - else{ newstab.newstabstatus=login.newsViewType; - if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} - else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + + //print("imageUrls "+JSON.stringify(imageUrls)+" newsstack.depth:"+newsStack.depth); + //newsStack.push({item:"qrc:/qml/newsqml/MessageSend.qml",properties:{attachImageURLs:[imageUrl]}}) +// var imagePicker = Qt.createQmlObject('import QtQuick 2.0; import "qrc:/qml/genericqml";'+ +// osSettings.imagePickQml+'{multiple : true; onReady: {'+ +// 'if(imageUrls.length==1){root.currentIndex=0;newstab.active=true;root.uploadSignal(imageUrls)} else{'+ +// ' root.currentIndex=2;fotostab.active=true;'+ +// 'root.uploadSignal(imageUrls)};}}',newstab,"imagePicker"); + //SystemDispatcher.setInitialized(); + if(root.news.length>0){showNews(root.news)} + else{ newstab.newstabstatus=login.newsViewType; + if(login.newsViewType=="Timeline"){Newsjs.newsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + else{Newsjs.chatsfromdb(db,login.username,function(dbnews){showNews(dbnews)})} + } } - }} + } } } diff --git a/source-linux/qml/newsqml/Newsitem.qml b/source-linux/qml/newsqml/Newsitem.qml index c046235..77f59fa 100644 --- a/source-linux/qml/newsqml/Newsitem.qml +++ b/source-linux/qml/newsqml/Newsitem.qml @@ -34,15 +34,15 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import "qrc:/js/news.js" as Newsjs import "qrc:/js/helper.js" as Helperjs - +import "qrc:/qml/genericqml" Item { id: newsitem width: parent.width height:toprow.height+friendicaActivities.height+controlrow.height+1//Math.max((itemMessage.height+topFlow.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm) + property int itemindex: index property string attending: "" - property int itemindex: index onAttendingChanged: {attendLabel.visible=true; attendLabel.text= qsTr("attending: ")+ qsTr(attending)} @@ -145,8 +145,9 @@ Item { textFormat: Text.RichText text:Qt.atob(newsitemobject.statusnet_html) width: newsitem.width-8*mm-2 - height: implicitHeight + height: Math.min(implicitHeight,3/4*root.height) wrapMode: Text.Wrap + clip:true onLinkActivated:{ Qt.openUrlExternally(link)} Component.onCompleted:{ @@ -159,6 +160,29 @@ Item { } } } + BlueButton{ + width: newsitem.width-8*mm-2 + height:10*mm + anchors.bottom: itemMessage.bottom + visible: itemMessage.implicitHeight>3/4*root.height + text:"\uf078" + fontColor:"grey" + border.color: "transparent" + gradient: Gradient { + GradientStop { position: 0.0; color: "transparent" } + GradientStop { position: 0.5; color: "white" } + } + radius:0 + onClicked: { + if (text=="\uf078"){ + itemMessage.height=itemMessage.implicitHeight+10*mm;text="\uf077" + } else { + itemMessage.height=Math.min(itemMessage.implicitHeight,3/4*root.height); + text="\uf078"; + newsView.positionViewAtIndex(index,ListView.Beginning); + } + } + } } } } diff --git a/source-linux/qml/photoqml/ImageUploadDialog.qml b/source-linux/qml/photoqml/ImageUploadDialog.qml index 23b37c6..ca379f0 100644 --- a/source-linux/qml/photoqml/ImageUploadDialog.qml +++ b/source-linux/qml/photoqml/ImageUploadDialog.qml @@ -64,12 +64,10 @@ Rectangle{ imageUploadModel.append({"imageUrl":url,"description":""}) } - z:2 - border.color: "grey" - width: parent.width-4*mm - height:parent.height-12*mm - x:2*mm - y:10*mm + //border.color: "grey" + y:1 + width:root.width-mm + height:root.height-5*mm property string directory: "" Connections{ @@ -95,8 +93,12 @@ Rectangle{ anchors.topMargin: 1*mm anchors.right: parent.right anchors.rightMargin: 1*mm - spacing:mm - + spacing:5*mm + Text{ + font.pixelSize: 3.5*mm + font.bold: true + text:qsTr("Upload to album") + } // BlueButton{ // id:permButton // text: ((contact_allow.length==0)&&(contact_deny.length==0)&&(group_allow.length==0)&&(group_deny.length==0))?"\uf09c":"\uf023" @@ -109,7 +111,9 @@ Rectangle{ BlueButton{ id:closeButton text: "\uf057" - onClicked:{imageDialog.destroy()} + onClicked:{photoStack.pop(); + //imageDialog.destroy() + } } } @@ -250,6 +254,9 @@ Rectangle{ albumModel.append({"text":storedAlbums[n]})} })} catch (e){print(e)} + if(attachImageURLs.length>0){ + for (var n in attachImageURLs){attachImage(attachImageURLs[n])} + } } // BusyIndicator{ // id: imageBusy diff --git a/source-linux/qml/photoqml/PhotoTab.qml b/source-linux/qml/photoqml/PhotoTab.qml index c42b23e..de6e1da 100644 --- a/source-linux/qml/photoqml/PhotoTab.qml +++ b/source-linux/qml/photoqml/PhotoTab.qml @@ -37,7 +37,11 @@ import "qrc:/js/helper.js" as Helperjs import "qrc:/qml/photoqml" import "qrc:/qml/genericqml" -Rectangle { + +StackView{ + id: photoStack + anchors.fill:parent + initialItem:Rectangle { id:fotorectangle y:1 width:root.width-mm @@ -48,15 +52,15 @@ Rectangle { property bool remoteContact: false onNewimagesChanged:{ - if(newimages.length>0){ + if(fotorectangle.newimages.length>0){ //print("newimages "+JSON.stringify(newimages)); var ownimagelist=[]; Helperjs.readField("album",root.db,"imageData",root.login.username,function(albums){ - for (var i=0;i