mirror of
https://github.com/nextcloud/android.git
synced 2024-11-26 23:28:42 +03:00
Add status to sharee
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
parent
153aa77b41
commit
dbd9e6fe9b
24 changed files with 794 additions and 46 deletions
7
drawable_resources/user-status-away.svg
Normal file
7
drawable_resources/user-status-away.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" enable-background="new 0 0 24 24" version="1.1" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" fill="none" />
|
||||
<path
|
||||
d="m10.615 2.1094c-4.8491 0.68106-8.6152 4.8615-8.6152 9.8906 0 5.5 4.5 10 10 10 5.0292 0 9.2096-3.7661 9.8906-8.6152-1.4654 1.601-3.5625 2.6152-5.8906 2.6152-4.4 0-8-3.6-8-8 0-2.3281 1.0143-4.4252 2.6152-5.8906z"
|
||||
fill="#f4a331" />
|
||||
</svg>
|
After Width: | Height: | Size: 449 B |
6
drawable_resources/user-status-dnd.svg
Normal file
6
drawable_resources/user-status-dnd.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg width="24" height="24" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="m12 2c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10z" fill="#ed484c" />
|
||||
<path d="m8 10h8c1.108 0 2 0.892 2 2s-0.892 2-2 2h-8c-1.108 0-2-0.892-2-2s0.892-2 2-2z" fill="#fdffff"
|
||||
stroke-linecap="round" stroke-linejoin="round" stroke-width="2" style="paint-order:stroke markers fill" />
|
||||
</svg>
|
After Width: | Height: | Size: 473 B |
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
|
@ -58,7 +58,7 @@ fi
|
|||
if [[ $4 = "all" ]]; then
|
||||
scripts/runAllScreenshotCombinations "noCI" "$1" "-Pandroid.testInstrumentationRunnerArguments.class=$class$method"
|
||||
else
|
||||
./gradlew gplayDebugExecuteScreenshotTests $record \
|
||||
./gradlew --offline gplayDebugExecuteScreenshotTests $record \
|
||||
-Pandroid.testInstrumentationRunnerArguments.annotation=com.owncloud.android.utils.ScreenshotTest \
|
||||
-Pandroid.testInstrumentationRunnerArguments.class=$class$method \
|
||||
$darkMode \
|
||||
|
|
BIN
src/androidTest/assets/christine.jpg
Normal file
BIN
src/androidTest/assets/christine.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
src/androidTest/assets/paulette.jpg
Normal file
BIN
src/androidTest/assets/paulette.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.9 KiB |
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.providers
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.client.TestActivity
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class UsersAndGroupsSearchProviderIT : AbstractOnServerIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
fun searchUser() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
shortSleep()
|
||||
|
||||
activity.runOnUiThread {
|
||||
// fragment.search("Admin")
|
||||
}
|
||||
|
||||
longSleep()
|
||||
}
|
||||
}
|
|
@ -22,16 +22,21 @@
|
|||
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
|
||||
import com.nextcloud.client.TestActivity
|
||||
import com.nextcloud.client.account.StatusType
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.ui.TextDrawable
|
||||
import com.owncloud.android.utils.BitmapUtils
|
||||
import com.owncloud.android.utils.DisplayUtils
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
|
||||
class AvatarIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
@ -60,4 +65,124 @@ class AvatarIT : AbstractIT() {
|
|||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showAvatarsWithStatus() {
|
||||
val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
|
||||
val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = AvatarTestFragment()
|
||||
|
||||
val paulette = BitmapFactory.decodeFile(getFile("paulette.jpg").absolutePath)
|
||||
val christine = BitmapFactory.decodeFile(getFile("christine.jpg").absolutePath)
|
||||
val textBitmap = BitmapUtils.drawableToBitmap(TextDrawable.createNamedAvatar("Admin", avatarRadius))
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
runOnUiThread {
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.online, "😘", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.online, "☁️", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.online, "🌴️", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.online, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.dnd, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.away, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.offline, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.online, "😘", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.online, "☁️", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.online, "🌴️", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.online, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.dnd, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.away, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.offline, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -34,12 +35,14 @@ import com.owncloud.android.R
|
|||
import com.owncloud.android.ui.TextDrawable
|
||||
|
||||
internal class AvatarTestFragment : Fragment() {
|
||||
lateinit var list: LinearLayout
|
||||
lateinit var list1: LinearLayout
|
||||
lateinit var list2: LinearLayout
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view: View = inflater.inflate(R.layout.avatar_fragment, null)
|
||||
|
||||
list = view.findViewById(R.id.avatar_list)
|
||||
list1 = view.findViewById(R.id.avatar_list1)
|
||||
list2 = view.findViewById(R.id.avatar_list2)
|
||||
|
||||
return view
|
||||
}
|
||||
|
@ -54,7 +57,25 @@ internal class AvatarTestFragment : Fragment() {
|
|||
layoutParams.setMargins(margin, margin, margin, margin)
|
||||
imageView.layoutParams = layoutParams
|
||||
|
||||
list.addView(imageView)
|
||||
list1.addView(imageView)
|
||||
}
|
||||
|
||||
fun addBitmap(bitmap: Bitmap, width: Int, list: Int, targetContext: Context) {
|
||||
val margin = padding
|
||||
val imageView = ImageView(targetContext)
|
||||
imageView.setImageBitmap(bitmap)
|
||||
|
||||
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(width, width)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
|
||||
layoutParams.setMargins(margin, margin, margin, margin)
|
||||
imageView.layoutParams = layoutParams
|
||||
|
||||
if (list == 1) {
|
||||
list1.addView(imageView)
|
||||
} else {
|
||||
list2.addView(imageView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -21,11 +21,9 @@
|
|||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.Manifest
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.nextcloud.client.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
|
@ -51,9 +49,6 @@ class FileDetailSharingFragmentIT : AbstractIT() {
|
|||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
|
||||
lateinit var file: OCFile
|
||||
lateinit var folder: OCFile
|
||||
lateinit var activity: TestActivity
|
||||
|
|
|
@ -18,10 +18,24 @@
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/avatar_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/avatar_list1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/avatar_list2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
24
src/main/java/com/nextcloud/client/account/Status.kt
Normal file
24
src/main/java/com/nextcloud/client/account/Status.kt
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.client.account
|
||||
|
||||
internal class Status(val status: Enum<StatusType>, val message: String, val icon: String, val clearAt: String)
|
27
src/main/java/com/nextcloud/client/account/StatusType.kt
Normal file
27
src/main/java/com/nextcloud/client/account/StatusType.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.account
|
||||
|
||||
enum class StatusType {
|
||||
online, offline, dnd, away, unknown
|
||||
}
|
|
@ -27,25 +27,38 @@ import android.content.Context;
|
|||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nextcloud.client.account.Status;
|
||||
import com.nextcloud.client.account.StatusType;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.shares.GetShareesRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.shares.ShareType;
|
||||
import com.owncloud.android.ui.TextDrawable;
|
||||
import com.owncloud.android.utils.BitmapUtils;
|
||||
import com.owncloud.android.utils.ErrorMessageAdapter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -72,6 +85,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
private static final String[] COLUMNS = {
|
||||
BaseColumns._ID,
|
||||
SearchManager.SUGGEST_COLUMN_TEXT_1,
|
||||
SearchManager.SUGGEST_COLUMN_TEXT_2,
|
||||
SearchManager.SUGGEST_COLUMN_ICON_1,
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_DATA
|
||||
};
|
||||
|
@ -229,7 +243,8 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
Iterator<JSONObject> namesIt = names.iterator();
|
||||
JSONObject item;
|
||||
String displayName;
|
||||
int icon = 0;
|
||||
String subline = null;
|
||||
Object icon = 0;
|
||||
Uri dataUri;
|
||||
int count = 0;
|
||||
while (namesIt.hasNext()) {
|
||||
|
@ -237,13 +252,26 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
dataUri = null;
|
||||
displayName = null;
|
||||
String userName = item.getString(GetShareesRemoteOperation.PROPERTY_LABEL);
|
||||
String name = item.isNull("name") ? "" : item.getString("name");
|
||||
JSONObject value = item.getJSONObject(GetShareesRemoteOperation.NODE_VALUE);
|
||||
ShareType type = ShareType.fromValue(value.getInt(GetShareesRemoteOperation.PROPERTY_SHARE_TYPE));
|
||||
String shareWith = value.getString(GetShareesRemoteOperation.PROPERTY_SHARE_WITH);
|
||||
|
||||
Status status;
|
||||
JSONObject statusObject = item.optJSONObject("status");
|
||||
|
||||
if (statusObject != null) {
|
||||
status = new Status(StatusType.valueOf(statusObject.getString("status")),
|
||||
statusObject.isNull("message") ? "" : statusObject.getString("message"),
|
||||
statusObject.isNull("icon") ? "" : statusObject.getString("icon"),
|
||||
statusObject.isNull("clearAt") ? "" : statusObject.getString("clearAt"));
|
||||
} else {
|
||||
status = new Status(StatusType.unknown, "", "", "");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case GROUP:
|
||||
displayName = getContext().getString(R.string.share_group_clarification, userName);
|
||||
displayName = userName;
|
||||
icon = R.drawable.ic_group;
|
||||
dataUri = Uri.withAppendedPath(groupBaseUri, shareWith);
|
||||
break;
|
||||
|
@ -254,30 +282,47 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
dataUri = Uri.withAppendedPath(remoteBaseUri, shareWith);
|
||||
|
||||
if (userName.equals(shareWith)) {
|
||||
displayName = getContext().getString(R.string.share_remote_clarification, userName);
|
||||
displayName = name;
|
||||
subline = getContext().getString(R.string.remote);
|
||||
} else {
|
||||
String[] uriSplitted = shareWith.split("@");
|
||||
displayName = getContext().getString(R.string.share_known_remote_clarification,
|
||||
userName, uriSplitted[uriSplitted.length - 1]);
|
||||
displayName = name;
|
||||
subline = getContext().getString(R.string.share_known_remote_on_clarification,
|
||||
uriSplitted[uriSplitted.length - 1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case USER:
|
||||
displayName = userName;
|
||||
icon = R.drawable.ic_user;
|
||||
subline = status.getMessage().isEmpty() ? null : status.getMessage();
|
||||
Uri.Builder builder =
|
||||
Uri.parse("content://com.nextcloud.android.providers.UsersAndGroupsSearchProvider/icon")
|
||||
.buildUpon();
|
||||
|
||||
builder.appendQueryParameter("shareWith", shareWith);
|
||||
builder.appendQueryParameter("displayName", displayName);
|
||||
builder.appendQueryParameter("status", status.getStatus().toString());
|
||||
|
||||
if (!TextUtils.isEmpty(status.getIcon()) && !"null".equals(status.getIcon())) {
|
||||
builder.appendQueryParameter("icon", status.getIcon());
|
||||
}
|
||||
|
||||
icon = builder.build();
|
||||
|
||||
dataUri = Uri.withAppendedPath(userBaseUri, shareWith);
|
||||
break;
|
||||
|
||||
case EMAIL:
|
||||
icon = R.drawable.ic_email;
|
||||
displayName = getContext().getString(R.string.share_email_clarification, userName);
|
||||
displayName = name;
|
||||
subline = shareWith;
|
||||
dataUri = Uri.withAppendedPath(emailBaseUri, shareWith);
|
||||
break;
|
||||
|
||||
case ROOM:
|
||||
icon = R.drawable.ic_chat_bubble;
|
||||
displayName = getContext().getString(R.string.share_room_clarification, userName);
|
||||
icon = R.drawable.ic_talk;
|
||||
displayName = userName;
|
||||
dataUri = Uri.withAppendedPath(roomBaseUri, shareWith);
|
||||
break;
|
||||
|
||||
|
@ -295,6 +340,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
response.newRow()
|
||||
.add(count++) // BaseColumns._ID
|
||||
.add(displayName) // SearchManager.SUGGEST_COLUMN_TEXT_1
|
||||
.add(subline) // SearchManager.SUGGEST_COLUMN_TEXT_2
|
||||
.add(icon) // SearchManager.SUGGEST_COLUMN_ICON_1
|
||||
.add(dataUri);
|
||||
}
|
||||
|
@ -324,6 +370,56 @@ public class UsersAndGroupsSearchProvider extends ContentProvider {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
|
||||
|
||||
String userId = uri.getQueryParameter("shareWith");
|
||||
String displayName = uri.getQueryParameter("displayName");
|
||||
String accountName = accountManager.getUser().getAccountName();
|
||||
String serverName = accountName.substring(accountName.lastIndexOf('@') + 1);
|
||||
|
||||
String eTag = arbitraryDataProvider.getValue(userId + "@" + serverName, ThumbnailsCacheManager.AVATAR);
|
||||
String avatarKey = "a_" + userId + "_" + serverName + "_" + eTag;
|
||||
|
||||
StatusType status = StatusType.valueOf(uri.getQueryParameter("status"));
|
||||
String icon = uri.getQueryParameter("icon");
|
||||
|
||||
Bitmap avatarBitmap = ThumbnailsCacheManager.getBitmapFromDiskCache(avatarKey);
|
||||
|
||||
if (avatarBitmap == null) {
|
||||
float avatarRadius = getContext().getResources().getDimension(R.dimen.list_item_avatar_icon_radius);
|
||||
avatarBitmap = BitmapUtils.drawableToBitmap(TextDrawable.createNamedAvatar(displayName, avatarRadius));
|
||||
}
|
||||
|
||||
Bitmap avatar = BitmapUtils.createAvatarWithStatus(avatarBitmap, status, icon, getContext());
|
||||
|
||||
// create a file to write bitmap data
|
||||
File f = new File(getContext().getCacheDir(), "test");
|
||||
try {
|
||||
f.createNewFile();
|
||||
|
||||
//Convert bitmap to byte array
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
avatar.compress(Bitmap.CompressFormat.PNG, 90, bos);
|
||||
byte[] bitmapData = bos.toByteArray();
|
||||
|
||||
//write the bytes in file
|
||||
try (FileOutputStream fos = new FileOutputStream(f)) {
|
||||
fos.write(bitmapData);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log_OC.e(TAG, "File not found: " + e.getMessage());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG, "Error opening file: " + e.getMessage());
|
||||
}
|
||||
|
||||
return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message
|
||||
*
|
||||
|
|
127
src/main/java/com/owncloud/android/ui/StatusDrawable.java
Normal file
127
src/main/java/com/owncloud/android/ui/StatusDrawable.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.owncloud.android.utils.BitmapUtils;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
/**
|
||||
* A Drawable object that draws a status
|
||||
*/
|
||||
public class StatusDrawable extends Drawable {
|
||||
private String mText = null;
|
||||
private @DrawableRes int icon = -1;
|
||||
private Paint mTextPaint;
|
||||
private final Paint mBackground;
|
||||
private final float radius;
|
||||
private Context context;
|
||||
|
||||
public StatusDrawable(@DrawableRes int icon, float size, Context context) {
|
||||
radius = size;
|
||||
this.icon = icon;
|
||||
this.context = context;
|
||||
|
||||
mBackground = new Paint();
|
||||
mBackground.setStyle(Paint.Style.FILL);
|
||||
mBackground.setAntiAlias(true);
|
||||
mBackground.setColor(Color.argb(200, 255, 255, 255));
|
||||
}
|
||||
|
||||
public StatusDrawable(BitmapUtils.Color color, float size) {
|
||||
radius = size;
|
||||
|
||||
mBackground = new Paint();
|
||||
mBackground.setStyle(Paint.Style.FILL);
|
||||
mBackground.setAntiAlias(true);
|
||||
mBackground.setColor(Color.argb(color.a, color.r, color.g, color.b));
|
||||
}
|
||||
|
||||
public StatusDrawable(String icon, float size) {
|
||||
mText = icon;
|
||||
radius = size;
|
||||
|
||||
mBackground = new Paint();
|
||||
mBackground.setStyle(Paint.Style.FILL);
|
||||
mBackground.setAntiAlias(true);
|
||||
mBackground.setColor(Color.argb(200, 255, 255, 255));
|
||||
|
||||
mTextPaint = new Paint();
|
||||
mTextPaint.setColor(Color.WHITE);
|
||||
mTextPaint.setTextSize(size);
|
||||
mTextPaint.setAntiAlias(true);
|
||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color
|
||||
* filter (set via setColorFilter) a circular background with a user's first character.
|
||||
*
|
||||
* @param canvas The canvas to draw into
|
||||
*/
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (mBackground != null) {
|
||||
canvas.drawCircle(radius, radius, radius, mBackground);
|
||||
}
|
||||
|
||||
if (mText != null) {
|
||||
mTextPaint.setTextSize(1.6f * radius);
|
||||
canvas.drawText(mText, radius, radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2), mTextPaint);
|
||||
}
|
||||
|
||||
if (icon != -1) {
|
||||
Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), icon, null);
|
||||
drawable.setBounds(0,
|
||||
0,
|
||||
(int) (2 * radius),
|
||||
(int) (2 * radius));
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mTextPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
mTextPaint.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* ownCloud Android client application
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* @author Tobias Kaminsiky
|
||||
* @author Tobias Kaminsky
|
||||
* @author Chris Narkiewicz
|
||||
* Copyright (C) 2016 ownCloud Inc.
|
||||
* Copyright (C) 2018 Andy Scherzinger
|
||||
|
@ -35,7 +35,6 @@ import com.nextcloud.client.account.UserAccountManager;
|
|||
import com.owncloud.android.utils.BitmapUtils;
|
||||
import com.owncloud.android.utils.NextcloudServer;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -65,6 +64,8 @@ public class TextDrawable extends Drawable {
|
|||
*/
|
||||
private float mRadius;
|
||||
|
||||
private boolean bigText = false;
|
||||
|
||||
/**
|
||||
* Create a TextDrawable with the given radius.
|
||||
*
|
||||
|
@ -79,44 +80,43 @@ public class TextDrawable extends Drawable {
|
|||
mBackground = new Paint();
|
||||
mBackground.setStyle(Paint.Style.FILL);
|
||||
mBackground.setAntiAlias(true);
|
||||
mBackground.setColor(Color.rgb(color.r, color.g, color.b));
|
||||
mBackground.setColor(Color.argb(color.a, color.r, color.g, color.b));
|
||||
|
||||
mTextPaint = new Paint();
|
||||
mTextPaint.setColor(Color.WHITE);
|
||||
mTextPaint.setTextSize(radius);
|
||||
mTextPaint.setAntiAlias(true);
|
||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||
|
||||
setBounds(0, 0, (int) radius * 2, (int) radius * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the
|
||||
* given radius.
|
||||
* creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the given
|
||||
* radius.
|
||||
*
|
||||
* @param account user account
|
||||
* @param radiusInDp the circle's radius
|
||||
* @return the avatar as a TextDrawable
|
||||
* @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
|
||||
*/
|
||||
@NonNull
|
||||
@NextcloudServer(max = 12)
|
||||
public static TextDrawable createAvatar(Account account, float radiusInDp) throws
|
||||
NoSuchAlgorithmException {
|
||||
public static TextDrawable createAvatar(Account account, float radiusInDp) {
|
||||
String username = UserAccountManager.getDisplayName(account);
|
||||
return createNamedAvatar(username, radiusInDp);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the
|
||||
* given radius.
|
||||
* creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the given
|
||||
* radius.
|
||||
*
|
||||
* @param userId userId to use
|
||||
* @param radiusInDp the circle's radius
|
||||
* @return the avatar as a TextDrawable
|
||||
* @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
|
||||
*/
|
||||
@NonNull
|
||||
@NextcloudServer(max = 12)
|
||||
public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) throws NoSuchAlgorithmException {
|
||||
public static TextDrawable createAvatarByUserId(String userId, float radiusInDp) {
|
||||
return createNamedAvatar(userId, radiusInDp);
|
||||
}
|
||||
|
||||
|
@ -127,10 +127,9 @@ public class TextDrawable extends Drawable {
|
|||
* @param name the name
|
||||
* @param radiusInDp the circle's radius
|
||||
* @return the avatar as a TextDrawable
|
||||
* @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
|
||||
*/
|
||||
@NonNull
|
||||
public static TextDrawable createNamedAvatar(String name, float radiusInDp) throws NoSuchAlgorithmException {
|
||||
public static TextDrawable createNamedAvatar(String name, float radiusInDp) {
|
||||
BitmapUtils.Color color = BitmapUtils.usernameToColor(name);
|
||||
return new TextDrawable(extractCharsFromDisplayName(name), color, radiusInDp);
|
||||
}
|
||||
|
@ -160,6 +159,11 @@ public class TextDrawable extends Drawable {
|
|||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
canvas.drawCircle(mRadius, mRadius, mRadius, mBackground);
|
||||
|
||||
if (bigText) {
|
||||
mTextPaint.setTextSize(1.8f * mRadius);
|
||||
}
|
||||
|
||||
canvas.drawText(mText, mRadius, mRadius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2), mTextPaint);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@ import com.owncloud.android.databinding.FileDetailsShareShareItemBinding;
|
|||
import com.owncloud.android.lib.resources.shares.OCShare;
|
||||
import com.owncloud.android.ui.TextDrawable;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -95,7 +93,7 @@ class ShareViewHolder extends RecyclerView.ViewHolder {
|
|||
private void setImage(ImageView avatar, String name, @DrawableRes int fallback) {
|
||||
try {
|
||||
avatar.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadiusDimension));
|
||||
} catch (NoSuchAlgorithmException | StringIndexOutOfBoundsException e) {
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
avatar.setImageResource(fallback);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.components
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawable
|
||||
|
||||
class AvatarWithStatus(val roundedBitmapDrawable: RoundedBitmapDrawable) : Drawable() {
|
||||
private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) }
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
val width: Int = 100
|
||||
val height: Int = 100
|
||||
val radius: Float = Math.min(width, height).toFloat() / 2f
|
||||
|
||||
// Draw a red circle in the center
|
||||
//canvas.drawBitmap(roundedBitmapDrawable.bitmap!!, 0f, 0f, null)
|
||||
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint)
|
||||
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getOpacity(): Int = PixelFormat.OPAQUE
|
||||
}
|
|
@ -76,6 +76,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
|
@ -713,4 +714,10 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
|
|||
private boolean canReshare(OCShare share) {
|
||||
return (share.getPermissions() & SHARE_PERMISSION_FLAG) > 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void search(String query) {
|
||||
SearchView searchView = getView().findViewById(R.id.searchView);
|
||||
searchView.setQuery(query, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,27 @@
|
|||
*/
|
||||
package com.owncloud.android.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.nextcloud.client.account.StatusType;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.StatusDrawable;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
|
@ -194,12 +202,19 @@ public final class BitmapUtils {
|
|||
return resultBitmap;
|
||||
}
|
||||
|
||||
public static Color usernameToColor(String name) throws NoSuchAlgorithmException {
|
||||
public static Color usernameToColor(String name) {
|
||||
String hash = name.toLowerCase(Locale.ROOT);
|
||||
|
||||
// already a md5 hash?
|
||||
if (!hash.matches("([0-9a-f]{4}-?){8}$")) {
|
||||
try {
|
||||
hash = md5(hash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
int color = getResources().getColor(R.color.primary_dark);
|
||||
return new Color(android.graphics.Color.red(color),
|
||||
android.graphics.Color.green(color),
|
||||
android.graphics.Color.blue(color));
|
||||
}
|
||||
}
|
||||
|
||||
hash = hash.replaceAll("[^0-9a-f]", "");
|
||||
|
@ -279,6 +294,7 @@ public final class BitmapUtils {
|
|||
}
|
||||
|
||||
public static class Color {
|
||||
public int a = 255;
|
||||
public int r;
|
||||
public int g;
|
||||
public int b;
|
||||
|
@ -289,6 +305,13 @@ public final class BitmapUtils {
|
|||
this.b = b;
|
||||
}
|
||||
|
||||
public Color(int a, int r, int g, int b) {
|
||||
this.a = a;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Color)) {
|
||||
|
@ -358,9 +381,16 @@ public final class BitmapUtils {
|
|||
|
||||
Bitmap bitmap;
|
||||
if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
|
||||
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
if (drawable.getBounds().width() > 0 && drawable.getBounds().height() > 0) {
|
||||
bitmap = Bitmap.createBitmap(drawable.getBounds().width(),
|
||||
drawable.getBounds().height(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
} else {
|
||||
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
|
||||
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
} else {
|
||||
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||
drawable.getIntrinsicHeight(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
|
@ -384,6 +414,90 @@ public final class BitmapUtils {
|
|||
imageView);
|
||||
}
|
||||
|
||||
public static Bitmap createAvatarWithStatus(Bitmap avatar, StatusType status, String icon, Context context) {
|
||||
float avatarRadius = getResources().getDimension(R.dimen.list_item_avatar_icon_radius);
|
||||
int width = DisplayUtils.convertDpToPixel(2 * avatarRadius, context);
|
||||
|
||||
Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(output);
|
||||
|
||||
// avatar
|
||||
Bitmap croppedBitmap = getCroppedBitmap(avatar, width);
|
||||
|
||||
canvas.drawBitmap(croppedBitmap, 0f, 0f, null);
|
||||
|
||||
// status
|
||||
int statusSize = width / 4;
|
||||
|
||||
StatusDrawable statusDrawable;
|
||||
if (TextUtils.isEmpty(icon)) {
|
||||
switch (status) {
|
||||
case dnd:
|
||||
statusDrawable = new StatusDrawable(R.drawable.ic_user_status_dnd, statusSize, context);
|
||||
statusDrawable.setBounds(width / 2,
|
||||
width / 2,
|
||||
width,
|
||||
width);
|
||||
break;
|
||||
|
||||
case online:
|
||||
statusDrawable = new StatusDrawable(new Color(255, 73, 179, 130), statusSize);
|
||||
statusDrawable.setBounds(width,
|
||||
width,
|
||||
width,
|
||||
width);
|
||||
break;
|
||||
|
||||
case away:
|
||||
statusDrawable = new StatusDrawable(R.drawable.ic_user_status_away, statusSize, context);
|
||||
statusDrawable.setBounds(width / 2,
|
||||
width / 2,
|
||||
width,
|
||||
width);
|
||||
break;
|
||||
|
||||
default:
|
||||
// do not show
|
||||
statusDrawable = null;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
statusDrawable = new StatusDrawable(icon, statusSize);
|
||||
statusDrawable.setBounds(width / 2,
|
||||
width / 2,
|
||||
width,
|
||||
width);
|
||||
}
|
||||
|
||||
if (statusDrawable != null) {
|
||||
canvas.translate(width / 2f, width / 2f);
|
||||
statusDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* from https://stackoverflow.com/a/12089127
|
||||
*/
|
||||
private static Bitmap getCroppedBitmap(Bitmap bitmap, int width) {
|
||||
Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(output);
|
||||
int color = -0xbdbdbe;
|
||||
Paint paint = new Paint();
|
||||
Rect rect = new Rect(0, 0, width, width);
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawARGB(0, 0, 0, 0);
|
||||
paint.setColor(color);
|
||||
|
||||
canvas.drawCircle(width / 2f, width / 2f, width / 2f, paint);
|
||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||
|
||||
canvas.drawBitmap(Bitmap.createScaledBitmap(bitmap, width, width, false), rect, rect, paint);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static Resources getResources() {
|
||||
return MainApp.getAppContext().getResources();
|
||||
}
|
||||
|
|
11
src/main/res/drawable/ic_talk.xml
Normal file
11
src/main/res/drawable/ic_talk.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:fillColor="#757575"
|
||||
android:pathData="M63.992,0.689C29.031,0.689 0.691,29.031 0.692,63.992c0,34.96 28.34,63.301 63.3,63.302 6.982,-0.014 13.881,-1.183 20.426,-3.43 4.317,-1.482 8.48,-3.433 12.411,-5.831 3.383,1.344 8.59,3.838 13.736,5.902 6.688,2.683 13.274,4.639 15.618,2.399 2.317,-2.212 0.703,-8.809 -1.647,-15.575 -2.046,-5.892 -4.649,-11.913 -5.701,-15.282 2.544,-4.415 4.535,-9.101 5.945,-13.954 1.648,-5.674 2.5,-11.574 2.512,-17.532C127.291,29.032 98.952,0.692 63.992,0.691ZM63.999,24.756l0.001,0c21.677,0 39.25,17.573 39.25,39.251 -0.001,21.677 -17.574,39.249 -39.251,39.249 -21.676,0 -39.249,-17.572 -39.25,-39.249 0,-21.678 17.573,-39.251 39.25,-39.251z"
|
||||
android:strokeWidth="4.78543139" />
|
||||
</vector>
|
32
src/main/res/drawable/ic_user_status_away.xml
Normal file
32
src/main/res/drawable/ic_user_status_away.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
~
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#f4a331"
|
||||
android:pathData="m10.615,2.1094c-4.8491,0.6811 -8.6152,4.8615 -8.6152,9.8906 0,5.5 4.5,10 10,10 5.0292,0 9.2096,-3.7661 9.8906,-8.6152 -1.4654,1.601 -3.5625,2.6152 -5.8906,2.6152 -4.4,0 -8,-3.6 -8,-8 0,-2.3281 1.0143,-4.4252 2.6152,-5.8906z" />
|
||||
</vector>
|
38
src/main/res/drawable/ic_user_status_dnd.xml
Normal file
38
src/main/res/drawable/ic_user_status_dnd.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<!--
|
||||
~
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#ed484c"
|
||||
android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10z" />
|
||||
<path
|
||||
android:fillColor="#fdffff"
|
||||
android:pathData="m8,10h8c1.108,0 2,0.892 2,2s-0.892,2 -2,2h-8c-1.108,0 -2,-0.892 -2,-2s0.892,-2 2,-2z"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2" />
|
||||
</vector>
|
|
@ -479,7 +479,7 @@
|
|||
<string name="share_remote_clarification">%1$s (remote)</string>
|
||||
<string name="share_email_clarification">%1$s (email)</string>
|
||||
<string name="share_room_clarification">%1$s (conversation)</string>
|
||||
<string name="share_known_remote_clarification">%1$s ( at %2$s )</string>
|
||||
<string name="share_known_remote_on_clarification">on %1$s</string>
|
||||
|
||||
<string name="share_privilege_unshare">Unshare</string>
|
||||
|
||||
|
@ -936,4 +936,5 @@
|
|||
<string name="link_share_file_drop">File drop (upload only)</string>
|
||||
<string name="could_not_retrieve_shares">Could not retrieve shares</string>
|
||||
<string name="failed_update_ui">Failed to update UI</string>
|
||||
<string name="remote">(remote)</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue