Add support for time stickers in image editor.
This commit is contained in:
parent
08ebca501b
commit
28f27915c5
29 changed files with 1254 additions and 23 deletions
BIN
app/src/main/assets/fonts/Hatsuishi-Regular.otf
Normal file
BIN
app/src/main/assets/fonts/Hatsuishi-Regular.otf
Normal file
Binary file not shown.
|
@ -62,7 +62,7 @@ class KeyboardStickerListAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
data class StickerHeader(override val packId: String, private val title: String?, private val titleResource: Int?) : MappingModel<StickerHeader>, HasPackId {
|
||||
data class StickerHeader(override val packId: String, private val title: String?, private val titleResource: Int?) : MappingModel<StickerHeader>, HasPackId, Header {
|
||||
fun getTitle(context: Context): String {
|
||||
return title ?: context.resources.getString(titleResource ?: R.string.StickerManagementAdapter_untitled)
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ class KeyboardStickerListAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
interface Header
|
||||
interface HasPackId {
|
||||
val packId: String
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import java.util.Optional
|
|||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
class StickerKeyboardPageFragment :
|
||||
open class StickerKeyboardPageFragment :
|
||||
LoggingFragment(R.layout.keyboard_pager_sticker_page_fragment),
|
||||
KeyboardStickerListAdapter.EventListener,
|
||||
StickerRolloverTouchListener.RolloverEventListener,
|
||||
|
@ -39,9 +39,9 @@ class StickerKeyboardPageFragment :
|
|||
DatabaseObserver.Observer,
|
||||
View.OnLayoutChangeListener {
|
||||
|
||||
private lateinit var stickerList: RecyclerView
|
||||
private lateinit var stickerListAdapter: KeyboardStickerListAdapter
|
||||
private lateinit var layoutManager: GridLayoutManager
|
||||
protected lateinit var stickerList: RecyclerView
|
||||
protected lateinit var stickerListAdapter: KeyboardStickerListAdapter
|
||||
protected lateinit var layoutManager: GridLayoutManager
|
||||
private lateinit var listTouchListener: StickerRolloverTouchListener
|
||||
private lateinit var stickerPacksRecycler: RecyclerView
|
||||
private lateinit var appBarLayout: AppBarLayout
|
||||
|
@ -63,7 +63,7 @@ class StickerKeyboardPageFragment :
|
|||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
val model: Optional<MappingModel<*>> = stickerListAdapter.getModel(position)
|
||||
if (model.isPresent && model.get() is KeyboardStickerListAdapter.StickerHeader) {
|
||||
if (model.isPresent && model.get() is KeyboardStickerListAdapter.Header) {
|
||||
return spanCount
|
||||
}
|
||||
return 1
|
||||
|
@ -124,15 +124,19 @@ class StickerKeyboardPageFragment :
|
|||
viewModel.refreshStickers()
|
||||
}
|
||||
|
||||
private fun updateStickerList(stickers: MappingModelList) {
|
||||
open fun updateStickerList(stickers: MappingModelList) {
|
||||
if (firstLoad) {
|
||||
stickerListAdapter.submitList(stickers) { layoutManager.scrollToPositionWithOffset(1, 0) }
|
||||
stickerListAdapter.submitList(stickers, this::scrollOnLoad)
|
||||
firstLoad = false
|
||||
} else {
|
||||
stickerListAdapter.submitList(stickers)
|
||||
}
|
||||
}
|
||||
|
||||
open fun scrollOnLoad() {
|
||||
layoutManager.scrollToPositionWithOffset(1, 0)
|
||||
}
|
||||
|
||||
private fun onTabSelected(stickerPack: KeyboardStickerPackListAdapter.StickerPack) {
|
||||
scrollTo(stickerPack.packRecord.packId)
|
||||
viewModel.selectPack(stickerPack.packRecord.packId)
|
||||
|
|
|
@ -59,6 +59,10 @@ import org.thoughtcrime.securesms.mms.PushMediaConstraints;
|
|||
import org.thoughtcrime.securesms.mms.SentMediaQuality;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.AnalogClockStickerRenderer;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.DigitalClockStickerRenderer;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.FeatureSticker;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.TappableRenderer;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ParcelUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
|
@ -413,10 +417,25 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
|||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == RESULT_OK && requestCode == SELECT_STICKER_REQUEST_CODE && data != null) {
|
||||
final Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
UriGlideRenderer renderer = new UriGlideRenderer(uri, true, imageMaxWidth, imageMaxHeight);
|
||||
EditorElement element = new EditorElement(renderer, EditorModel.Z_STICKERS);
|
||||
Renderer renderer = null;
|
||||
if (data.hasExtra(ImageEditorStickerSelectActivity.EXTRA_FEATURE_STICKER)) {
|
||||
FeatureSticker sticker = FeatureSticker.fromType(data.getStringExtra(ImageEditorStickerSelectActivity.EXTRA_FEATURE_STICKER));
|
||||
switch (sticker) {
|
||||
case DIGITAL_CLOCK:
|
||||
renderer = new DigitalClockStickerRenderer(System.currentTimeMillis());
|
||||
break;
|
||||
case ANALOG_CLOCK:
|
||||
renderer = new AnalogClockStickerRenderer(System.currentTimeMillis());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
final Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
renderer = new UriGlideRenderer(uri, true, imageMaxWidth, imageMaxHeight);
|
||||
}
|
||||
}
|
||||
if (renderer != null) {
|
||||
EditorElement element = new EditorElement(renderer, EditorModel.Z_STICKERS);
|
||||
imageEditorView.getModel().addElementCentered(element, 0.4f);
|
||||
setCurrentSelection(element);
|
||||
hasMadeAnEditThisSession = true;
|
||||
|
@ -1002,6 +1021,9 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
|||
if (editorElement.getRenderer() instanceof MultiLineTextRenderer) {
|
||||
setTextElement(editorElement, (ColorableRenderer) editorElement.getRenderer(), imageEditorView.isTextEditing());
|
||||
} else {
|
||||
if (editorElement.getRenderer() instanceof TappableRenderer) {
|
||||
((TappableRenderer) editorElement.getRenderer()).onTapped();
|
||||
}
|
||||
imageEditorHud.setMode(ImageEditorHudV2.Mode.MOVE_STICKER);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -17,14 +16,17 @@ import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
|||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel;
|
||||
import org.thoughtcrime.securesms.keyboard.sticker.StickerKeyboardPageFragment;
|
||||
import org.thoughtcrime.securesms.keyboard.sticker.StickerSearchDialogFragment;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.FeatureSticker;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.ScribbleStickersFragment;
|
||||
import org.thoughtcrime.securesms.stickers.StickerEventListener;
|
||||
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
public final class ImageEditorStickerSelectActivity extends AppCompatActivity implements StickerEventListener, MediaKeyboard.MediaKeyboardListener, StickerKeyboardPageFragment.Callback {
|
||||
public final class ImageEditorStickerSelectActivity extends AppCompatActivity implements StickerEventListener, MediaKeyboard.MediaKeyboardListener, StickerKeyboardPageFragment.Callback, ScribbleStickersFragment.Callback {
|
||||
|
||||
public static final String EXTRA_FEATURE_STICKER = "imageEditor.featureSticker";
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(@NonNull Context newBase) {
|
||||
|
@ -36,12 +38,6 @@ public final class ImageEditorStickerSelectActivity extends AppCompatActivity im
|
|||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.scribble_select_new_sticker_activity);
|
||||
|
||||
KeyboardPagerViewModel keyboardPagerViewModel = new ViewModelProvider(this).get(KeyboardPagerViewModel.class);
|
||||
keyboardPagerViewModel.setOnlyPage(KeyboardPage.STICKER);
|
||||
|
||||
MediaKeyboard mediaKeyboard = findViewById(R.id.emoji_drawer);
|
||||
mediaKeyboard.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,4 +83,14 @@ public final class ImageEditorStickerSelectActivity extends AppCompatActivity im
|
|||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFeatureSticker(FeatureSticker featureSticker) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_FEATURE_STICKER, featureSticker.getType());
|
||||
setResult(RESULT_OK, intent);
|
||||
|
||||
ViewUtil.hideKeyboard(this, findViewById(android.R.id.content));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.SystemClock
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.util.toLocalDateTime
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Animatable drawable of an analog clock. You can set a time, or start the animation to animate
|
||||
* the current time.
|
||||
*/
|
||||
class AnalogClockStickerDrawable(val context: Context) : Drawable(), Animatable {
|
||||
|
||||
private var clockFace: Drawable = AppCompatResources.getDrawable(context, R.drawable.clock_face_1)!!
|
||||
private var minuteHand: Drawable = AppCompatResources.getDrawable(context, R.drawable.clock_minute_hand_1)!!
|
||||
private var hourHand: Drawable = AppCompatResources.getDrawable(context, R.drawable.clock_hour_hand_1)!!
|
||||
private var clockCenter: Drawable? = null
|
||||
|
||||
/** Percentage of hour hand height that should shoot past the center point **/
|
||||
private var hourOffset = 0.28f
|
||||
|
||||
/** Percentage of minute hand height that should shoot past the center point **/
|
||||
private var minuteOffset = 0.2f
|
||||
|
||||
private var animating = false
|
||||
private var displayStyle = Style.STANDARD
|
||||
|
||||
private var time: Long? = null
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
clockFace.draw(canvas)
|
||||
|
||||
val now = time?.toLocalDateTime() ?: LocalDateTime.now()
|
||||
val hourDeg = computeHourRotationDeg(now)
|
||||
val minuteDeg = computeMinuteRotationDeg(now)
|
||||
|
||||
canvas.save()
|
||||
canvas.rotate(hourDeg, bounds.exactCenterX(), bounds.exactCenterY())
|
||||
hourHand.draw(canvas)
|
||||
canvas.restore()
|
||||
|
||||
canvas.save()
|
||||
canvas.rotate(minuteDeg, bounds.exactCenterX(), bounds.exactCenterY())
|
||||
minuteHand.draw(canvas)
|
||||
canvas.restore()
|
||||
|
||||
if (animating) {
|
||||
scheduleSelf(this::invalidateSelf, SystemClock.uptimeMillis() + 1000)
|
||||
}
|
||||
|
||||
clockCenter?.draw(canvas)
|
||||
}
|
||||
|
||||
fun nextFace() {
|
||||
setStyle(displayStyle.next())
|
||||
}
|
||||
|
||||
fun setStyle(style: Style) {
|
||||
displayStyle = style
|
||||
when (displayStyle) {
|
||||
Style.STANDARD -> clockFace1()
|
||||
Style.BLOCKY -> clockFace2()
|
||||
Style.LIGHT -> clockFace3()
|
||||
Style.GREEN -> clockFace4()
|
||||
}
|
||||
onBoundsChange(bounds)
|
||||
}
|
||||
|
||||
fun getStyle(): Style {
|
||||
return displayStyle
|
||||
}
|
||||
|
||||
fun setTime(newTime: Long?) {
|
||||
time = newTime
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
private fun clockFace1() {
|
||||
clockFace = AppCompatResources.getDrawable(context, R.drawable.clock_face_1)!!
|
||||
minuteHand = AppCompatResources.getDrawable(context, R.drawable.clock_minute_hand_1)!!
|
||||
hourHand = AppCompatResources.getDrawable(context, R.drawable.clock_hour_hand_1)!!
|
||||
clockCenter = null
|
||||
|
||||
hourOffset = 0.28f
|
||||
minuteOffset = 0.2f
|
||||
}
|
||||
|
||||
private fun clockFace2() {
|
||||
clockFace = AppCompatResources.getDrawable(context, R.drawable.clock_face_2)!!
|
||||
minuteHand = AppCompatResources.getDrawable(context, R.drawable.clock_minute_hand_2)!!
|
||||
hourHand = AppCompatResources.getDrawable(context, R.drawable.clock_hour_hand_2)!!
|
||||
clockCenter = null
|
||||
|
||||
hourOffset = 0.238f
|
||||
minuteOffset = 0.1623f
|
||||
}
|
||||
|
||||
private fun clockFace3() {
|
||||
clockFace = AppCompatResources.getDrawable(context, R.drawable.clock_face_3)!!
|
||||
minuteHand = AppCompatResources.getDrawable(context, R.drawable.clock_minute_hand_3)!!
|
||||
hourHand = AppCompatResources.getDrawable(context, R.drawable.clock_hour_hand_3)!!
|
||||
clockCenter = null
|
||||
|
||||
hourOffset = 0f
|
||||
minuteOffset = 0f
|
||||
}
|
||||
|
||||
private fun clockFace4() {
|
||||
clockFace = AppCompatResources.getDrawable(context, R.drawable.clock_face_4)!!
|
||||
minuteHand = AppCompatResources.getDrawable(context, R.drawable.clock_minute_hand_4)!!
|
||||
hourHand = AppCompatResources.getDrawable(context, R.drawable.clock_hour_hand_4)!!
|
||||
clockCenter = AppCompatResources.getDrawable(context, R.drawable.clock_center_cover_4)
|
||||
|
||||
hourOffset = 0f
|
||||
minuteOffset = 0f
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) = Unit
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
val dimen = min(bounds.width(), bounds.height())
|
||||
val scale: Float = dimen.toFloat() / clockFace.intrinsicWidth.toFloat()
|
||||
val centerX = bounds.centerX()
|
||||
val centerY = bounds.centerY()
|
||||
|
||||
val hourW = (hourHand.intrinsicWidth * scale).roundToInt()
|
||||
val hourH = (hourHand.intrinsicHeight * scale).roundToInt()
|
||||
|
||||
val minuteW = (minuteHand.intrinsicWidth * scale).roundToInt()
|
||||
val minuteH = (minuteHand.intrinsicHeight * scale).roundToInt()
|
||||
|
||||
if (bounds.width() > bounds.height()) {
|
||||
val diff = (bounds.width() - bounds.height()) / 2
|
||||
clockFace.setBounds(bounds.left + diff, bounds.top, bounds.right - diff, bounds.bottom)
|
||||
} else {
|
||||
val diff = (bounds.height() - bounds.width()) / 2
|
||||
clockFace.setBounds(bounds.left, bounds.top - diff, bounds.right, bounds.bottom + diff)
|
||||
}
|
||||
val hourVertical = (hourH * hourOffset).roundToInt()
|
||||
val minuteVertical = (minuteH * minuteOffset).roundToInt()
|
||||
hourHand.setBounds(centerX - hourW / 2, (centerY - hourH + hourVertical), centerX + hourW / 2, centerY + hourVertical)
|
||||
minuteHand.setBounds(centerX - minuteW / 2, (centerY - minuteH + minuteVertical), centerX + minuteW / 2, centerY + minuteVertical)
|
||||
|
||||
val centerVal = clockCenter
|
||||
if (centerVal != null) {
|
||||
val centerW = (centerVal.intrinsicWidth * scale).roundToInt()
|
||||
val centerH = (centerVal.intrinsicHeight * scale).roundToInt()
|
||||
|
||||
centerVal.setBounds(centerX - centerW / 2, centerY - centerH / 2, centerX + centerW / 2, centerY + centerH / 2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIntrinsicWidth(): Int {
|
||||
return clockFace.intrinsicWidth
|
||||
}
|
||||
|
||||
override fun getIntrinsicHeight(): Int {
|
||||
return clockFace.intrinsicHeight
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
animating = true
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
animating = false
|
||||
unscheduleSelf(this::invalidateSelf)
|
||||
}
|
||||
|
||||
override fun isRunning(): Boolean {
|
||||
return animating
|
||||
}
|
||||
|
||||
private fun computeHourRotationDeg(localDateTime: LocalDateTime): Float {
|
||||
val hour = localDateTime.hour % 12
|
||||
val minute = localDateTime.minute
|
||||
val seconds = localDateTime.second
|
||||
|
||||
return 360f * (hour + (minute / 60f) + (seconds / 3600f)) / 12f
|
||||
}
|
||||
|
||||
private fun computeMinuteRotationDeg(localDateTime: LocalDateTime): Float {
|
||||
val minute = localDateTime.minute
|
||||
val seconds = localDateTime.second
|
||||
|
||||
return 360f * (minute + (seconds / 60f)) / 60f
|
||||
}
|
||||
|
||||
enum class Style(val type: Int) {
|
||||
STANDARD(0),
|
||||
BLOCKY(1),
|
||||
LIGHT(2),
|
||||
GREEN(3);
|
||||
|
||||
fun next(): Style {
|
||||
val values = Style.values()
|
||||
|
||||
return values[(values.indexOf(this) + 1) % values.size]
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromType(type: Int) = Style.values().first { it.type == type }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import org.signal.imageeditor.core.Bounds
|
||||
import org.signal.imageeditor.core.RendererContext
|
||||
import org.signal.imageeditor.core.SelectableRenderer
|
||||
import org.signal.imageeditor.core.renderers.InvalidateableRenderer
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
|
||||
/**
|
||||
* Analog clock sticker renderer for the image editor.
|
||||
*/
|
||||
class AnalogClockStickerRenderer
|
||||
@JvmOverloads constructor(
|
||||
val time: Long,
|
||||
val style: AnalogClockStickerDrawable.Style = AnalogClockStickerDrawable.Style.STANDARD
|
||||
) : InvalidateableRenderer(), SelectableRenderer, TappableRenderer {
|
||||
|
||||
private val clockStickerDrawable = AnalogClockStickerDrawable(ApplicationDependencies.getApplication())
|
||||
private val insetBounds = Rect(
|
||||
Bounds.FULL_BOUNDS.left.toInt(),
|
||||
Bounds.FULL_BOUNDS.top.toInt(),
|
||||
Bounds.FULL_BOUNDS.right.toInt(),
|
||||
Bounds.FULL_BOUNDS.bottom.toInt()
|
||||
).apply { inset(261, 261) }
|
||||
|
||||
init {
|
||||
clockStickerDrawable.bounds = insetBounds
|
||||
clockStickerDrawable.setTime(time)
|
||||
clockStickerDrawable.setStyle(style)
|
||||
}
|
||||
|
||||
override fun onTapped() {
|
||||
clockStickerDrawable.nextFace()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onSelected(selected: Boolean) {
|
||||
}
|
||||
|
||||
override fun getSelectionBounds(bounds: RectF) {
|
||||
bounds.set(Bounds.FULL_BOUNDS)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeLong(time)
|
||||
dest.writeInt(clockStickerDrawable.getStyle().type)
|
||||
}
|
||||
|
||||
override fun render(rendererContext: RendererContext) {
|
||||
clockStickerDrawable.draw(rendererContext.canvas)
|
||||
}
|
||||
|
||||
override fun hitTest(x: Float, y: Float): Boolean {
|
||||
return Bounds.FULL_BOUNDS.contains(x, y)
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<AnalogClockStickerRenderer> {
|
||||
override fun createFromParcel(parcel: Parcel): AnalogClockStickerRenderer {
|
||||
return AnalogClockStickerRenderer(parcel.readLong(), AnalogClockStickerDrawable.Style.fromType(parcel.readInt()))
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<AnalogClockStickerRenderer?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
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.Rect
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.SystemClock
|
||||
import android.text.TextPaint
|
||||
import android.text.format.DateFormat
|
||||
import org.thoughtcrime.securesms.util.toLocalDateTime
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Animatable drawable of a digital clock. You can set a time, or start the animation to animate
|
||||
* the current time. Supports 12/24 hr time.
|
||||
*/
|
||||
class DigitalClockStickerDrawable(
|
||||
val context: Context,
|
||||
private var displayStyle: Style = Style.LIGHT_NO_BG
|
||||
) :
|
||||
Drawable(), Animatable {
|
||||
|
||||
companion object {
|
||||
private const val BG_PADDING = 40f
|
||||
private const val BG_CORNER_RADIUS = 40f
|
||||
private const val AM_PM_SPACING = 7f
|
||||
private const val LIGHT_BG_COLOR = 0x66FFFFFF
|
||||
private const val DARK_BG_COLOR = 0x66000000
|
||||
private const val RED_TEXT_COLOR = 0xFFFF4747.toInt()
|
||||
private const val TIME_TEXT_SIZE = 204f
|
||||
private const val AM_PM_TEXT_SIZE = 50f
|
||||
|
||||
/** Box dimensions that wrap the sticker. Dimensions are relative to this value from designs. */
|
||||
private const val STICKER_BOX_SIZE = 512f
|
||||
|
||||
/** Additional scaling factor as sticker is still small within the box */
|
||||
private const val STICKER_SCALING_ADJUSTMENT = 1.2f
|
||||
}
|
||||
|
||||
private val ampmTypeface = Typeface.createFromAsset(context.assets, "fonts/Inter-Medium.otf")
|
||||
private val digitTypeface = Typeface.createFromAsset(context.assets, "fonts/Hatsuishi-Regular.otf")
|
||||
|
||||
private var wrapped = false
|
||||
private var animating = false
|
||||
private var scale = 1f
|
||||
|
||||
private var time: Long? = null
|
||||
|
||||
private val digitPaint = TextPaint().apply {
|
||||
this.typeface = digitTypeface
|
||||
this.textSize = 204f
|
||||
this.textAlign = Paint.Align.LEFT
|
||||
this.color = Color.WHITE
|
||||
}
|
||||
|
||||
private val ampmPaint = TextPaint().apply {
|
||||
this.typeface = ampmTypeface
|
||||
this.textSize = 50f
|
||||
this.textAlign = Paint.Align.LEFT
|
||||
this.color = Color.WHITE
|
||||
}
|
||||
|
||||
private val bgPaint = Paint().apply {
|
||||
color = Color.WHITE
|
||||
}
|
||||
|
||||
init {
|
||||
setStyle(displayStyle)
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
val centerX = bounds.exactCenterX()
|
||||
val centerY = bounds.exactCenterY()
|
||||
|
||||
val timeMetrics = digitPaint.fontMetrics
|
||||
val now = time?.toLocalDateTime() ?: LocalDateTime.now()
|
||||
val is24Hours = DateFormat.is24HourFormat(context)
|
||||
val timeHeight = timeMetrics.bottom + timeMetrics.top + timeMetrics.leading
|
||||
val baseline = centerY - timeHeight / 2f
|
||||
if (is24Hours) {
|
||||
digitPaint.textAlign = Paint.Align.CENTER
|
||||
val timeStr = getHoursString(now)
|
||||
val width = digitPaint.measureText(timeStr)
|
||||
if (wrapped) {
|
||||
val bgCornerRadius = getBgCornerRadius()
|
||||
val bgPadding = getBgPadding()
|
||||
canvas.drawRoundRect(
|
||||
centerX - width / 2f - bgPadding,
|
||||
baseline + timeMetrics.top - bgPadding,
|
||||
centerX + width / 2f + bgPadding,
|
||||
baseline + timeMetrics.bottom + bgPadding,
|
||||
bgCornerRadius,
|
||||
bgCornerRadius,
|
||||
bgPaint
|
||||
)
|
||||
}
|
||||
canvas.drawText(timeStr, centerX, baseline, digitPaint)
|
||||
} else {
|
||||
digitPaint.textAlign = Paint.Align.LEFT
|
||||
val timeStr = getHoursString(now)
|
||||
val timeWidth = digitPaint.measureText(timeStr)
|
||||
val amPmStr = getAmPmString(now)
|
||||
val amPmWidth = ampmPaint.measureText(amPmStr)
|
||||
val ampmSpacing = AM_PM_SPACING * scale
|
||||
val totalWidth = timeWidth + amPmWidth + ampmSpacing
|
||||
|
||||
if (wrapped) {
|
||||
val bgPadding = getBgPadding()
|
||||
val bgCornerRadius = getBgCornerRadius()
|
||||
canvas.drawRoundRect(
|
||||
centerX - totalWidth / 2f - bgPadding,
|
||||
baseline + timeMetrics.top - bgPadding,
|
||||
centerX + totalWidth / 2f + bgPadding,
|
||||
baseline + timeMetrics.bottom + bgPadding,
|
||||
bgCornerRadius,
|
||||
bgCornerRadius,
|
||||
bgPaint
|
||||
)
|
||||
}
|
||||
|
||||
canvas.drawText(timeStr, centerX - totalWidth / 2f, baseline, digitPaint)
|
||||
canvas.drawText(amPmStr, centerX + ampmSpacing + timeWidth - (totalWidth / 2f), baseline, ampmPaint)
|
||||
}
|
||||
|
||||
if (animating) {
|
||||
scheduleSelf(this::invalidateSelf, SystemClock.uptimeMillis() + 1000)
|
||||
}
|
||||
}
|
||||
|
||||
fun nextStyle() {
|
||||
setStyle(displayStyle.next())
|
||||
}
|
||||
|
||||
fun setStyle(style: Style) {
|
||||
displayStyle = style
|
||||
when (style) {
|
||||
Style.LIGHT_NO_BG -> styleWhiteTextNoBg()
|
||||
Style.DARK_NO_BG -> styleBlackTextNoBg()
|
||||
Style.LIGHT -> styleLightWithBg()
|
||||
Style.DARK -> styleDarkWithBg()
|
||||
Style.DARK_WITH_RED_TEXT -> styleDarkWithRedText()
|
||||
}
|
||||
onBoundsChange(bounds)
|
||||
}
|
||||
|
||||
fun getStyle(): Style {
|
||||
return displayStyle
|
||||
}
|
||||
|
||||
private fun styleWhiteTextNoBg() {
|
||||
digitPaint.color = Color.WHITE
|
||||
ampmPaint.color = Color.WHITE
|
||||
wrapped = false
|
||||
}
|
||||
|
||||
private fun styleBlackTextNoBg() {
|
||||
digitPaint.color = Color.BLACK
|
||||
ampmPaint.color = Color.BLACK
|
||||
wrapped = false
|
||||
}
|
||||
|
||||
private fun styleLightWithBg() {
|
||||
digitPaint.color = Color.WHITE
|
||||
ampmPaint.color = Color.WHITE
|
||||
bgPaint.color = LIGHT_BG_COLOR
|
||||
wrapped = true
|
||||
}
|
||||
|
||||
private fun styleDarkWithBg() {
|
||||
digitPaint.color = Color.WHITE
|
||||
ampmPaint.color = Color.WHITE
|
||||
bgPaint.color = DARK_BG_COLOR
|
||||
wrapped = true
|
||||
}
|
||||
|
||||
private fun styleDarkWithRedText() {
|
||||
digitPaint.color = RED_TEXT_COLOR
|
||||
ampmPaint.color = RED_TEXT_COLOR
|
||||
bgPaint.color = DARK_BG_COLOR
|
||||
wrapped = true
|
||||
}
|
||||
|
||||
private fun getBgPadding(): Float {
|
||||
return BG_PADDING * scale
|
||||
}
|
||||
|
||||
private fun getBgCornerRadius(): Float {
|
||||
return BG_CORNER_RADIUS * scale
|
||||
}
|
||||
|
||||
private fun getAmPmString(time: LocalDateTime): String {
|
||||
return DateTimeFormatter.ofPattern("a", Locale.getDefault()).format(time)
|
||||
}
|
||||
|
||||
private fun getHoursString(time: LocalDateTime): String {
|
||||
return if (!DateFormat.is24HourFormat(context)) {
|
||||
DateTimeFormatter.ofPattern("h:mm", Locale.getDefault()).format(time)
|
||||
} else {
|
||||
DateTimeFormatter.ofPattern("H:mm", Locale.getDefault()).format(time)
|
||||
}
|
||||
}
|
||||
|
||||
fun setTime(newTime: Long?) {
|
||||
time = newTime
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) = Unit
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
|
||||
|
||||
override fun getOpacity(): Int {
|
||||
return PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
val dimension = min(bounds.width(), bounds.height())
|
||||
scale = (dimension / STICKER_BOX_SIZE) * STICKER_SCALING_ADJUSTMENT
|
||||
digitPaint.textSize = scale * TIME_TEXT_SIZE
|
||||
ampmPaint.textSize = scale * AM_PM_TEXT_SIZE
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
animating = true
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
animating = false
|
||||
unscheduleSelf(this::invalidateSelf)
|
||||
}
|
||||
|
||||
override fun isRunning(): Boolean {
|
||||
return animating
|
||||
}
|
||||
|
||||
enum class Style(val type: Int) {
|
||||
LIGHT_NO_BG(0),
|
||||
DARK_NO_BG(1),
|
||||
LIGHT(2),
|
||||
DARK(3),
|
||||
DARK_WITH_RED_TEXT(4);
|
||||
|
||||
fun next(): Style {
|
||||
val values = Style.values()
|
||||
|
||||
return values[(values.indexOf(this) + 1) % values.size]
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromType(type: Int) = Style.values().first { it.type == type }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import org.signal.imageeditor.core.Bounds
|
||||
import org.signal.imageeditor.core.RendererContext
|
||||
import org.signal.imageeditor.core.SelectableRenderer
|
||||
import org.signal.imageeditor.core.renderers.InvalidateableRenderer
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
|
||||
/**
|
||||
* Analog clock sticker renderer for the image editor.
|
||||
*/
|
||||
class DigitalClockStickerRenderer
|
||||
@JvmOverloads constructor(
|
||||
val time: Long,
|
||||
val style: DigitalClockStickerDrawable.Style = DigitalClockStickerDrawable.Style.LIGHT_NO_BG
|
||||
) : InvalidateableRenderer(), SelectableRenderer, TappableRenderer {
|
||||
|
||||
private val clockStickerDrawable = DigitalClockStickerDrawable(ApplicationDependencies.getApplication())
|
||||
private val insetBounds = Rect(
|
||||
Bounds.FULL_BOUNDS.left.toInt(),
|
||||
Bounds.FULL_BOUNDS.top.toInt(),
|
||||
Bounds.FULL_BOUNDS.right.toInt(),
|
||||
Bounds.FULL_BOUNDS.bottom.toInt()
|
||||
).apply { inset(261, 261) }
|
||||
|
||||
init {
|
||||
clockStickerDrawable.bounds = insetBounds
|
||||
clockStickerDrawable.setTime(time)
|
||||
clockStickerDrawable.setStyle(style)
|
||||
}
|
||||
|
||||
override fun onTapped() {
|
||||
clockStickerDrawable.nextStyle()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onSelected(selected: Boolean) {
|
||||
}
|
||||
|
||||
override fun getSelectionBounds(bounds: RectF) {
|
||||
bounds.set(Bounds.FULL_BOUNDS)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeLong(time)
|
||||
dest.writeInt(clockStickerDrawable.getStyle().type)
|
||||
}
|
||||
|
||||
override fun render(rendererContext: RendererContext) {
|
||||
clockStickerDrawable.draw(rendererContext.canvas)
|
||||
}
|
||||
|
||||
override fun hitTest(x: Float, y: Float): Boolean {
|
||||
return Bounds.FULL_BOUNDS.contains(x, y)
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<DigitalClockStickerRenderer> {
|
||||
override fun createFromParcel(parcel: Parcel): DigitalClockStickerRenderer {
|
||||
return DigitalClockStickerRenderer(
|
||||
parcel.readLong(),
|
||||
DigitalClockStickerDrawable.Style.fromType(parcel.readInt())
|
||||
)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<DigitalClockStickerRenderer?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
/**
|
||||
* Types of feature rich stickers for the image editor
|
||||
*/
|
||||
enum class FeatureSticker(val type: String) {
|
||||
DIGITAL_CLOCK("digital_clock"),
|
||||
ANALOG_CLOCK("analog_clock") ;
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromType(type: String) = FeatureSticker.values().first { it.type == type }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.keyboard.sticker.KeyboardStickerListAdapter
|
||||
import org.thoughtcrime.securesms.keyboard.sticker.StickerKeyboardPageFragment
|
||||
import org.thoughtcrime.securesms.util.Throttler
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModelList
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||
|
||||
/**
|
||||
* Sticker chooser fragment for the image editor. Implement the Callback for
|
||||
* both feature stickers, and regular stickers from StickerKeyboardPageFragment
|
||||
*/
|
||||
class ScribbleStickersFragment : StickerKeyboardPageFragment() {
|
||||
|
||||
interface Callback {
|
||||
fun onFeatureSticker(sticker: FeatureSticker)
|
||||
}
|
||||
|
||||
private val stickerThrottler: Throttler = Throttler(100)
|
||||
|
||||
private val featureStickerList: MappingModelList = MappingModelList(
|
||||
listOf(
|
||||
FeatureHeader(R.string.ScribbleStickersFragment__featured_stickers),
|
||||
FeatureStickerModel(FeatureSticker.ANALOG_CLOCK),
|
||||
FeatureStickerModel(FeatureSticker.DIGITAL_CLOCK)
|
||||
)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
stickerListAdapter.registerFactory(FeatureStickerModel::class.java, LayoutFactory(::FeatureStickerViewHolder, R.layout.sticker_keyboard_page_list_item))
|
||||
stickerListAdapter.registerFactory(FeatureHeader::class.java, LayoutFactory(::HeaderViewHolder, R.layout.sticker_grid_header))
|
||||
}
|
||||
|
||||
override fun updateStickerList(stickers: MappingModelList) {
|
||||
stickers.addAll(0, featureStickerList)
|
||||
super.updateStickerList(stickers)
|
||||
}
|
||||
|
||||
override fun scrollOnLoad() {
|
||||
}
|
||||
|
||||
private fun onStickerClick(featureSticker: FeatureSticker) {
|
||||
stickerThrottler.publish { findListener<Callback>()?.onFeatureSticker(featureSticker) }
|
||||
}
|
||||
|
||||
data class FeatureStickerModel(val featureSticker: FeatureSticker) : MappingModel<FeatureStickerModel> {
|
||||
|
||||
override fun areItemsTheSame(newItem: FeatureStickerModel): Boolean {
|
||||
return featureSticker == newItem.featureSticker
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(newItem: FeatureStickerModel): Boolean {
|
||||
return areItemsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FeatureStickerViewHolder(itemView: View) : MappingViewHolder<FeatureStickerModel>(itemView) {
|
||||
|
||||
private val image: ImageView = findViewById(R.id.sticker_keyboard_page_image)
|
||||
|
||||
override fun bind(model: FeatureStickerModel) {
|
||||
when (model.featureSticker) {
|
||||
FeatureSticker.ANALOG_CLOCK -> bindAnalogClock()
|
||||
FeatureSticker.DIGITAL_CLOCK -> bindDigitalClock()
|
||||
}
|
||||
image.setOnClickListener {
|
||||
onStickerClick(model.featureSticker)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAnalogClock() {
|
||||
val clockDrawable = AnalogClockStickerDrawable(image.context)
|
||||
clockDrawable.start()
|
||||
image.setImageDrawable(clockDrawable)
|
||||
}
|
||||
|
||||
private fun bindDigitalClock() {
|
||||
val clockDrawable = DigitalClockStickerDrawable(image.context)
|
||||
clockDrawable.start()
|
||||
image.setImageDrawable(clockDrawable)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
(image.drawable as? Animatable)?.start()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
(image.drawable as? Animatable)?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
data class FeatureHeader(private val titleResource: Int?) : MappingModel<FeatureHeader>, KeyboardStickerListAdapter.Header {
|
||||
fun getTitle(context: Context): String {
|
||||
return context.resources.getString(titleResource ?: R.string.StickerManagementAdapter_untitled)
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(newItem: FeatureHeader): Boolean {
|
||||
return titleResource == newItem.titleResource
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(newItem: FeatureHeader): Boolean {
|
||||
return areItemsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class HeaderViewHolder(itemView: View) : MappingViewHolder<FeatureHeader>(itemView) {
|
||||
|
||||
private val title: TextView = findViewById(R.id.sticker_grid_header_title)
|
||||
|
||||
override fun bind(model: FeatureHeader) {
|
||||
title.text = model.getTitle(context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.thoughtcrime.securesms.scribbles.stickers
|
||||
|
||||
import org.signal.imageeditor.core.Renderer
|
||||
|
||||
/**
|
||||
* A renderer that can handle a tap event
|
||||
*/
|
||||
interface TappableRenderer : Renderer {
|
||||
fun onTapped()
|
||||
}
|
15
app/src/main/res/drawable/clock_center_cover_4.xml
Normal file
15
app/src/main/res/drawable/clock_center_cover_4.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="42dp"
|
||||
android:height="42dp"
|
||||
android:viewportWidth="42"
|
||||
android:viewportHeight="42">
|
||||
<path
|
||||
android:pathData="M21,21m-21,0a21,21 0,1 1,42 0a21,21 0,1 1,-42 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M21,21m-21,0a21,21 0,1 1,42 0a21,21 0,1 1,-42 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M21,21m-21,0a21,21 0,1 1,42 0a21,21 0,1 1,-42 0"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
</vector>
|
64
app/src/main/res/drawable/clock_face_1.xml
Normal file
64
app/src/main/res/drawable/clock_face_1.xml
Normal file
|
@ -0,0 +1,64 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="600dp"
|
||||
android:height="600dp"
|
||||
android:viewportWidth="600"
|
||||
android:viewportHeight="600"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="M600,300C600,465.68 465.68,600 300,600C134.32,600 0,465.68 0,300C0,134.32 134.32,0 300,0C465.68,0 600,134.32 600,300Z"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.8"/>
|
||||
<path
|
||||
android:pathData="M310,295.31C310,300.84 305.52,305.31 300,305.31C294.48,305.31 290,300.84 290,295.31C290,289.79 294.48,285.31 300,285.31C305.52,285.31 310,289.79 310,295.31Z"
|
||||
android:fillColor="#FF0000"/>
|
||||
<path
|
||||
android:pathData="M320,300C320,311.05 311.05,320 300,320C288.95,320 280,311.05 280,300C280,288.95 288.95,280 300,280C311.05,280 320,288.95 320,300Z"
|
||||
android:fillColor="#FFBE20"/>
|
||||
<path
|
||||
android:pathData="M283.96,83.41V28.94H278.47C277.3,34.5 273.07,37.17 268.52,37.17H267.19V43.2H269.3C271.89,43.2 275.73,42.26 277.45,40.15V83.41H283.96Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M326.03,83.41V77.45H302.75C308.63,71.73 313.25,67.03 317.17,62.09C322.73,55.43 325.4,48.92 325.4,42.5C325.4,32.86 319.99,27.76 310.43,27.76C300.71,27.76 294.83,33.25 294.83,42.42C294.83,43.99 294.99,45.48 295.3,46.89L302.36,47.75C301.97,45.95 301.81,44.22 301.81,42.58C301.81,36.78 304.87,33.48 310.12,33.48C315.29,33.48 318.19,36.7 318.19,42.81C318.19,48.38 316.15,53.71 311.37,59.66C306.43,65.62 300.63,71.42 294.29,77.84V83.41H326.03Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M432.31,117.64V62.72H426.78C425.6,68.33 421.33,71.02 416.75,71.02H415.4V77.1H417.54C420.14,77.1 424.01,76.15 425.75,74.02V117.64H432.31Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M528.69,206.37V200.36H505.23C511.15,194.6 515.81,189.85 519.76,184.88C525.37,178.16 528.06,171.6 528.06,165.12C528.06,155.41 522.61,150.27 512.97,150.27C503.17,150.27 497.24,155.8 497.24,165.04C497.24,166.63 497.4,168.13 497.72,169.55L504.83,170.42C504.43,168.6 504.28,166.86 504.28,165.2C504.28,159.36 507.36,156.04 512.65,156.04C517.87,156.04 520.79,159.28 520.79,165.44C520.79,171.05 518.73,176.42 513.92,182.43C508.94,188.43 503.09,194.28 496.69,200.76V206.37H528.69Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M525.9,427.65H518.4V391.47H510.65L490.5,428.21V432.95H511.99V446.38H518.4V432.95H525.9V427.65ZM511.99,427.65H496.75C498.17,425.91 499.27,423.94 500.7,421.41L508.68,406.48C510.18,403.55 511.13,401.42 511.99,398.66H512.15C511.99,401.58 511.99,403.63 511.99,407.27V427.65Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M416.19,531.2C426.54,531.2 432.54,523.85 432.54,511.68C432.54,499.75 427.57,493.03 418.72,493.03C413.5,493.03 409.63,495.33 407.42,499.67L408.92,481.02H430.17V475.1H403.47L401.18,506.15L407.66,506.86C408.6,501.8 411.84,498.64 416.66,498.64C422.51,498.64 425.51,503.39 425.51,512.08C425.51,520.85 422.27,525.51 416.35,525.51C411.05,525.51 408.05,521.4 407.42,515.39L400.39,516.18C401.1,525.27 406.95,531.2 416.19,531.2Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M299.39,525.51C293.86,525.51 289.83,528.11 287.61,532.54C287.77,518.48 291.17,511.6 297.97,511.6C302.86,511.6 305.39,514.68 306.02,521.08L313.06,519.9C312.11,510.65 306.89,506.07 298.05,506.07C286.98,506.07 280.82,514.21 280.82,534.91C280.82,555.37 286.35,563.35 297.81,563.35C307.68,563.35 313.69,556.16 313.69,544.08C313.69,532.38 308.39,525.51 299.39,525.51ZM297.81,557.74C291.88,557.74 288.17,553 287.69,540.68C289.2,534.67 292.43,531.04 297.65,531.04C303.34,531.04 306.66,535.86 306.66,544.16C306.66,553.16 303.34,557.74 297.81,557.74Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M174.49,527.7V523.35C174.57,507.39 179.62,491.36 190.29,477.77V472.79H159.71V478.95H183.18C172.99,493.57 167.46,508.82 167.3,525.41V527.7H174.49Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M96.07,417.68C102.16,415.54 105.01,411.59 105.01,405.11C105.01,395.93 99,390.95 89.43,390.95C80.02,390.95 73.77,396.17 73.77,405.18C73.77,412.46 76.94,416.17 82.71,418.7C75.99,420.84 72.51,425.43 72.51,432.78C72.51,442.5 78.83,448.28 89.67,448.28C100.03,448.28 106.51,442.9 106.51,432.46C106.51,424.32 102.87,420.37 96.07,417.68ZM89.35,396.01C94.96,396.01 98.29,398.94 98.29,405.11C98.29,411.35 96.39,413.72 90.69,415.86L88.64,415.15C83.42,413.33 80.57,411.11 80.57,405.03C80.57,398.94 83.9,396.01 89.35,396.01ZM89.67,442.9C83.18,442.9 79.39,439.1 79.39,432.62C79.39,425.9 82,422.74 88.16,420.6L90.93,421.55C96.62,423.53 99.63,426.22 99.63,432.7C99.63,439.89 95.99,442.9 89.67,442.9Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M71.09,211.05V156.14H65.56C64.37,161.75 60.11,164.44 55.52,164.44H54.18V170.52H56.31C58.92,170.52 62.79,169.57 64.53,167.44V211.05H71.09Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M100.78,212.24C112.23,212.24 118.71,204.97 118.71,183.56C118.71,162.15 112.23,154.96 100.78,154.96C89.32,154.96 82.84,162.15 82.84,183.56C82.84,204.97 89.32,212.24 100.78,212.24ZM100.78,206.63C93.59,206.63 89.8,201.26 89.8,183.56C89.8,165.86 93.59,160.57 100.78,160.57C107.97,160.57 111.76,165.86 111.76,183.56C111.76,201.26 107.97,206.63 100.78,206.63Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M159.35,119.95V65.03H153.82C152.63,70.64 148.36,73.33 143.78,73.33H142.44V79.41H144.57C147.18,79.41 151.05,78.47 152.79,76.33V119.95H159.35Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M186.51,119.95V65.03H180.98C179.79,70.64 175.52,73.33 170.94,73.33H169.6V79.41H171.73C174.34,79.41 178.21,78.47 179.95,76.33V119.95H186.51Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M544.27,327.82C554.3,327.82 560.31,322.21 560.31,311.86C560.31,304.04 556.75,298.9 550.2,297.8C555.8,296.38 559.2,291.24 559.2,284.52C559.2,275.28 553.67,270.54 544.27,270.54C533.84,270.54 528.39,277.02 528.31,287.61L535.1,288.63C535.18,280.02 537.95,276.23 544.11,276.23C549.17,276.23 552.17,279.07 552.17,285C552.17,291.55 549.09,295.27 543.32,295.27H540.79V300.96H543.56C549.88,300.96 553.28,304.75 553.28,311.94C553.28,318.81 550.12,322.13 544.27,322.13C538.34,322.13 534.79,318.02 534.71,309.81L527.76,310.6C527.91,321.97 533.92,327.82 544.27,327.82Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M50.58,270.54C40.54,270.54 34.62,277.57 34.62,288.55C34.62,300.09 40.3,306.33 49.31,306.33C54.45,306.33 58.71,303.96 60.93,299.38C60.93,317.39 57.69,322.29 50.73,322.29C45.52,322.29 42.75,318.73 42.12,312.41L35.17,313.76C36.27,322.92 41.57,327.82 50.58,327.82C61.4,327.82 67.25,321.11 67.25,298.51C67.25,276.54 61.32,270.54 50.58,270.54ZM50.73,300.8C45.04,300.8 41.57,296.3 41.57,288.39C41.57,280.49 44.89,276.07 50.58,276.07C57.21,276.07 60.14,279.7 60.77,290.21C59.66,296.93 55.79,300.8 50.73,300.8Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
49
app/src/main/res/drawable/clock_face_2.xml
Normal file
49
app/src/main/res/drawable/clock_face_2.xml
Normal file
|
@ -0,0 +1,49 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="600dp"
|
||||
android:height="600dp"
|
||||
android:viewportWidth="600"
|
||||
android:viewportHeight="600"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M300,300m-300,0a300,300 0,1 1,600 0a300,300 0,1 1,-600 0"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillAlpha="0.5"/>
|
||||
<path
|
||||
android:pathData="M291,42h20v64h-20z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M291,494h20v64h-20z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M389.33,126.99l32,-55.43l17.32,10l-32,55.43z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M163.34,518.44l32,-55.43l17.32,10l-32,55.43z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M464.01,194.34l55.43,-32l10,17.32l-55.43,32z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M72.56,420.34l55.43,-32l10,17.32l-55.43,32z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M464.01,405.66l10,-17.32l55.43,32l-10,17.32z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M72.56,179.66l10,-17.32l55.43,32l-10,17.32z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M389.34,473.01l17.32,-10l32,55.43l-17.32,10z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M163.34,81.56l17.32,-10l32,55.43l-17.32,10z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M42,290h64v20h-64z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M494.53,290h64v20h-64z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
58
app/src/main/res/drawable/clock_face_3.xml
Normal file
58
app/src/main/res/drawable/clock_face_3.xml
Normal file
|
@ -0,0 +1,58 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="600dp"
|
||||
android:height="600dp"
|
||||
android:viewportWidth="600"
|
||||
android:viewportHeight="600"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="M300,300m-300,0a300,300 0,1 1,600 0a300,300 0,1 1,-600 0"
|
||||
android:strokeAlpha="0.3"
|
||||
android:fillColor="#fff"
|
||||
android:fillAlpha="0.3"/>
|
||||
<path
|
||||
android:pathData="m524.43,171h0c2.76,4.78 1.12,10.9 -3.66,13.66l-17.32,10c-4.78,2.76 -10.9,1.12 -13.66,-3.66h0c-2.76,-4.78 -1.12,-10.9 3.66,-13.66l17.32,-10c4.78,-2.76 10.9,-1.12 13.66,3.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m112.21,409h0c2.76,4.78 1.12,10.9 -3.66,13.66l-17.32,10c-4.78,2.76 -10.9,1.12 -13.66,-3.66h0c-2.76,-4.78 -1.12,-10.9 3.66,-13.66l17.32,-10c4.78,-2.76 10.9,-1.12 13.66,3.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m430,76.57h0c4.78,2.76 6.42,8.88 3.66,13.66l-10,17.32c-2.76,4.78 -8.88,6.42 -13.66,3.66h0c-4.78,-2.76 -6.42,-8.88 -3.66,-13.66l10,-17.32c2.76,-4.78 8.88,-6.42 13.66,-3.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m192,488.79h0c4.78,2.76 6.42,8.88 3.66,13.66l-10,17.32c-2.76,4.78 -8.88,6.42 -13.66,3.66h0c-4.78,-2.76 -6.42,-8.88 -3.66,-13.66l10,-17.32c2.76,-4.78 8.88,-6.42 13.66,-3.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m172,76.57h0c4.78,-2.76 10.9,-1.12 13.66,3.66l10,17.32c2.76,4.78 1.12,10.9 -3.66,13.66h0c-4.78,2.76 -10.9,1.12 -13.66,-3.66l-10,-17.32c-2.76,-4.78 -1.12,-10.9 3.66,-13.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m410,488.79h0c4.78,-2.76 10.9,-1.12 13.66,3.66l10,17.32c2.76,4.78 1.12,10.9 -3.66,13.66h0c-4.78,2.76 -10.9,1.12 -13.66,-3.66l-10,-17.32c-2.76,-4.78 -1.12,-10.9 3.66,-13.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m77.57,171h0c2.76,-4.78 8.88,-6.42 13.66,-3.66l17.32,10c4.78,2.76 6.42,8.88 3.66,13.66h0c-2.76,4.78 -8.88,6.42 -13.66,3.66l-17.32,-10c-4.78,-2.76 -6.42,-8.88 -3.66,-13.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m489.79,409h0c2.76,-4.78 8.88,-6.42 13.66,-3.66l17.32,10c4.78,2.76 6.42,8.88 3.66,13.66h0c-2.76,4.78 -8.88,6.42 -13.66,3.66l-17.32,-10c-4.78,-2.76 -6.42,-8.88 -3.66,-13.66Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M300,300m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||
android:fillColor="#ff0000"/>
|
||||
<path
|
||||
android:pathData="M300,300m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m285.47,80.64V26.17h-7.29c-1.49,5.49 -5.49,8.15 -10.42,8.15h-1.33v8.15h1.96c2.9,0 6.51,-0.86 8.23,-2.82v40.99h8.86Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m328.61,80.64v-7.52h-22.34c5.96,-5.41 10.35,-9.64 13.95,-14.11 5.33,-6.27 7.92,-12.38 7.92,-18.73 0,-10.27 -5.8,-15.28 -16.22,-15.28s-16.77,5.72 -16.77,15.13c0,1.49 0.16,2.9 0.47,4.31l9.25,1.02c-0.31,-1.72 -0.47,-3.45 -0.47,-5.02 0,-5.17 2.59,-8.15 7.13,-8.15 4.47,0 6.97,2.82 6.97,8.31 0,5.41 -1.88,10.27 -6.43,15.75 -4.47,5.33 -10.11,10.74 -17.24,17.32v6.97h33.78Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m300.28,536.57c-5.45,0 -9.24,2.29 -11.46,6.24 0.16,-12.88 3.16,-18.41 9.09,-18.41 4.27,0 6.48,2.77 7.11,8.45l9.09,-1.42c-0.95,-9.4 -6.4,-14.06 -16.04,-14.06 -11.61,0 -18.25,8.14 -18.25,28.76 0,20.31 6.01,28.52 18.09,28.52 10.43,0 16.83,-7.43 16.83,-19.67 0,-11.93 -5.61,-18.41 -14.46,-18.41ZM297.75,567.54c-5.29,0 -8.53,-4.43 -8.93,-16.04 1.5,-4.98 4.42,-7.9 8.85,-7.9 4.98,0 7.9,4.11 7.9,11.69 0,8.22 -2.92,12.25 -7.82,12.25Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m553.66,329.74c10.75,0 17.15,-5.85 17.15,-16.2 0,-7.59 -3.56,-12.56 -10.35,-13.67 5.85,-1.42 9.4,-6.56 9.4,-13.12 0,-9.4 -5.93,-14.3 -16.12,-14.3 -10.98,0 -17.07,6.32 -17.15,17.22l9.09,1.26c0.08,-7.98 2.53,-11.3 7.82,-11.3 4.43,0 6.95,2.45 6.95,7.9 0,5.85 -2.77,9.24 -7.98,9.24h-2.45v6.95h2.61c5.77,0 8.77,3.24 8.77,9.72 0,6.01 -2.77,9.01 -7.74,9.01 -5.14,0 -8.22,-3.71 -8.22,-11.3l-9.32,1.03c0.16,11.61 6.56,17.54 17.54,17.54Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m45.99,272.46c-10.74,0 -16.99,7.19 -16.99,18.33 0,11.38 5.77,17.78 15.01,17.78 4.82,0 8.93,-1.98 11.14,-6.01 -0.16,15.88 -3,20.07 -8.93,20.07 -4.5,0 -7.03,-3.24 -7.59,-8.85l-9.01,1.58c0.95,9.24 6.64,14.38 16.28,14.38 11.69,0 17.86,-6.72 17.86,-29.15 0,-22.04 -6.16,-28.13 -17.78,-28.13ZM46.15,301.62c-4.98,0 -8.06,-4.11 -8.06,-11.06 0,-7.27 2.92,-11.14 7.98,-11.14 5.93,0 8.53,3.32 9.01,13.75 -1.26,5.45 -4.66,8.45 -8.93,8.45Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
121
app/src/main/res/drawable/clock_face_4.xml
Normal file
121
app/src/main/res/drawable/clock_face_4.xml
Normal file
|
@ -0,0 +1,121 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="600dp"
|
||||
android:height="600dp"
|
||||
android:viewportWidth="600"
|
||||
android:viewportHeight="600"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M300,300m-300,0a300,300 0,1 1,600 0a300,300 0,1 1,-600 0"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillAlpha="0.8"/>
|
||||
<path
|
||||
android:pathData="M288,488L312,488A2,2 0,0 1,314 490L314,566A2,2 0,0 1,312 568L288,568A2,2 0,0 1,286 566L286,490A2,2 0,0 1,288 488z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M288,488L312,488A2,2 0,0 1,314 490L314,566A2,2 0,0 1,312 568L288,568A2,2 0,0 1,286 566L286,490A2,2 0,0 1,288 488z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M288,488L312,488A2,2 0,0 1,314 490L314,566A2,2 0,0 1,312 568L288,568A2,2 0,0 1,286 566L286,490A2,2 0,0 1,288 488z"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M34,286L110,286A2,2 0,0 1,112 288L112,312A2,2 0,0 1,110 314L34,314A2,2 0,0 1,32 312L32,288A2,2 0,0 1,34 286z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M34,286L110,286A2,2 0,0 1,112 288L112,312A2,2 0,0 1,110 314L34,314A2,2 0,0 1,32 312L32,288A2,2 0,0 1,34 286z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M34,286L110,286A2,2 0,0 1,112 288L112,312A2,2 0,0 1,110 314L34,314A2,2 0,0 1,32 312L32,288A2,2 0,0 1,34 286z"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M490,286L566,286A2,2 0,0 1,568 288L568,312A2,2 0,0 1,566 314L490,314A2,2 0,0 1,488 312L488,288A2,2 0,0 1,490 286z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M490,286L566,286A2,2 0,0 1,568 288L568,312A2,2 0,0 1,566 314L490,314A2,2 0,0 1,488 312L488,288A2,2 0,0 1,490 286z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M490,286L566,286A2,2 0,0 1,568 288L568,312A2,2 0,0 1,566 314L490,314A2,2 0,0 1,488 312L488,288A2,2 0,0 1,490 286z"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M424.05,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M424.05,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M424.05,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M178.03,513.06m-11.94,20.68a23.88,23.88 102.59,1 1,23.88 -41.35a23.88,23.88 102.59,1 1,-23.88 41.35"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M178.03,513.06m-11.94,20.68a23.88,23.88 102.59,1 1,23.88 -41.35a23.88,23.88 102.59,1 1,-23.88 41.35"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M178.03,513.06m-11.94,20.68a23.88,23.88 102.59,1 1,23.88 -41.35a23.88,23.88 102.59,1 1,-23.88 41.35"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M514.1,176.98m-22.32,8.47a23.88,23.88 127.36,1 1,44.64 -16.94a23.88,23.88 127.36,1 1,-44.64 16.94"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M514.1,176.98m-22.32,8.47a23.88,23.88 127.36,1 1,44.64 -16.94a23.88,23.88 127.36,1 1,-44.64 16.94"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M514.1,176.98m-22.32,8.47a23.88,23.88 127.36,1 1,44.64 -16.94a23.88,23.88 127.36,1 1,-44.64 16.94"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M87.98,423.01m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M87.98,423.01m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M87.98,423.01m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M514.09,423m-15.1,18.5a23.88,23.88 76.42,1 1,30.19 -36.99a23.88,23.88 76.42,1 1,-30.19 36.99"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M514.09,423m-15.1,18.5a23.88,23.88 76.42,1 1,30.19 -36.99a23.88,23.88 76.42,1 1,-30.19 36.99"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M514.09,423m-15.1,18.5a23.88,23.88 76.42,1 1,30.19 -36.99a23.88,23.88 76.42,1 1,-30.19 36.99"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M87.98,176.99m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M87.98,176.99m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M87.98,176.99m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M424.05,513.06m-20.68,11.94a23.88,23.88 77.26,1 1,41.35 -23.88a23.88,23.88 77.26,1 1,-41.35 23.88"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M424.05,513.06m-20.68,11.94a23.88,23.88 77.26,1 1,41.35 -23.88a23.88,23.88 77.26,1 1,-41.35 23.88"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M424.05,513.06m-20.68,11.94a23.88,23.88 77.26,1 1,41.35 -23.88a23.88,23.88 77.26,1 1,-41.35 23.88"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="M178.03,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M178.03,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M178.03,86.94m-23.88,0a23.88,23.88 0,1 1,47.75 0a23.88,23.88 0,1 1,-47.75 0"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
<path
|
||||
android:pathData="m301.83,115.87c-0.7,1.59 -2.95,1.59 -3.66,0l-32.44,-73.31c-0.58,-1.32 0.38,-2.81 1.83,-2.81h64.87c1.45,0 2.41,1.49 1.83,2.81l-32.44,73.31Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m301.83,115.87c-0.7,1.59 -2.95,1.59 -3.66,0l-32.44,-73.31c-0.58,-1.32 0.38,-2.81 1.83,-2.81h64.87c1.45,0 2.41,1.49 1.83,2.81l-32.44,73.31Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m301.83,115.87c-0.7,1.59 -2.95,1.59 -3.66,0l-32.44,-73.31c-0.58,-1.32 0.38,-2.81 1.83,-2.81h64.87c1.45,0 2.41,1.49 1.83,2.81l-32.44,73.31Z"
|
||||
android:fillColor="#d1ffc1"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/clock_hour_hand_1.xml
Normal file
9
app/src/main/res/drawable/clock_hour_hand_1.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="14"
|
||||
android:viewportHeight="200">
|
||||
<path
|
||||
android:pathData="M7,0L7,0A7,7 0,0 1,14 7L14,193A7,7 0,0 1,7 200L7,200A7,7 0,0 1,0 193L0,7A7,7 0,0 1,7 0z"
|
||||
android:fillColor="#FFBE20"/>
|
||||
</vector>
|
14
app/src/main/res/drawable/clock_hour_hand_2.xml
Normal file
14
app/src/main/res/drawable/clock_hour_hand_2.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="40dp"
|
||||
android:height="210dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="210"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="m5,0L0,210h40L35,0L5,0ZM20,176c8.84,0 16,-7.16 16,-16s-7.16,-16 -16,-16 -16,7.16 -16,16 7.16,16 16,16Z"
|
||||
android:strokeAlpha="0.98"
|
||||
android:fillColor="#fff"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.98"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/clock_hour_hand_3.xml
Normal file
9
app/src/main/res/drawable/clock_hour_hand_3.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="149dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="149">
|
||||
<path
|
||||
android:pathData="m10,149C4.48,149 0,144.52 0,139V10C0,4.48 4.48,0 10,0h0c5.52,0 10,4.48 10,10v129c0,5.52 -4.48,10 -10,10h0Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/clock_hour_hand_4.xml
Normal file
9
app/src/main/res/drawable/clock_hour_hand_4.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="138dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="138">
|
||||
<path
|
||||
android:pathData="m27.65,18.84c0.23,0.33 0.35,0.73 0.35,1.13v118.03H0V19.97c0,-0.41 0.12,-0.8 0.35,-1.13L12.35,1.4c0.8,-1.15 2.5,-1.15 3.3,0l12,17.44Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/clock_minute_hand_1.xml
Normal file
11
app/src/main/res/drawable/clock_minute_hand_1.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="280"
|
||||
android:width="10dp"
|
||||
android:height="280dp"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="M5 280C2.239 280 0 278.01 0 275.556V4.444C0 1.99 2.239 0 5 0h0c2.761 0 5 1.99 5 4.444v271.112c0 2.454 -2.239 4.444 -5 4.444h0Z"
|
||||
android:fillColor="#FFBE20" />
|
||||
</vector>
|
14
app/src/main/res/drawable/clock_minute_hand_2.xml
Normal file
14
app/src/main/res/drawable/clock_minute_hand_2.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="40dp"
|
||||
android:height="308dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="308"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="m5.76,0L0,308h40L34.24,0L5.76,0ZM20,273.94c8.84,0 16,-7.16 16,-16s-7.16,-16 -16,-16 -16,7.16 -16,16 7.16,16 16,16Z"
|
||||
android:strokeAlpha="0.98"
|
||||
android:fillColor="#fff"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.98"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/clock_minute_hand_3.xml
Normal file
11
app/src/main/res/drawable/clock_minute_hand_3.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="20dp"
|
||||
android:height="229dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="229"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="m10,229C4.48,229 0,224.52 0,219V10C0,4.48 4.48,0 10,0h0c5.52,0 10,4.48 10,10v209c0,5.52 -4.48,10 -10,10h0Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/clock_minute_hand_4.xml
Normal file
11
app/src/main/res/drawable/clock_minute_hand_4.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="28dp"
|
||||
android:height="267dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="267"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:pathData="m27.67,19.52c0.22,0.33 0.34,0.71 0.34,1.11v246.37H0V20.63c0,-0.4 0.12,-0.78 0.34,-1.11L12.34,1.5c0.79,-1.19 2.54,-1.19 3.33,0l12,18.02Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
13
app/src/main/res/drawable/ic_location_sticker.xml
Normal file
13
app/src/main/res/drawable/ic_location_sticker.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:pathData="M5.275,0L66.725,0A5.275,5.275 0,0 1,72 5.275L72,30.725A5.275,5.275 0,0 1,66.725 36L5.275,36A5.275,5.275 0,0 1,0 30.725L0,5.275A5.275,5.275 0,0 1,5.275 0z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M15.385,29.886C18.277,28.09 24.717,21.282 24.717,16.007C24.717,10.48 20.365,6 14.996,6C9.628,6 5.275,10.48 5.275,16.007C5.275,21.282 11.715,28.09 14.607,29.886C14.849,30.036 15.144,30.036 15.385,29.886ZM14.996,19.646C16.948,19.646 18.531,18.017 18.531,16.007C18.531,13.997 16.948,12.368 14.996,12.368C13.043,12.368 11.461,13.997 11.461,16.007C11.461,18.017 13.043,19.646 14.996,19.646Z"
|
||||
android:fillColor="#F44336"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -1,10 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.emoji.MediaKeyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/emoji_drawer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:name="org.thoughtcrime.securesms.scribbles.stickers.ScribbleStickersFragment"
|
||||
android:background="@color/core_grey_90"
|
||||
app:tabs_gravity="top" />
|
||||
|
|
|
@ -4478,6 +4478,9 @@
|
|||
<string name="ImageEditorHud__toggle_between_marker_and_highlighter">Toggle between marker and highlighter</string>
|
||||
<string name="ImageEditorHud__toggle_between_text_styles">Toggle between text styles</string>
|
||||
|
||||
<!-- Header for section of featured stickers (location/time stickers) -->
|
||||
<string name="ScribbleStickersFragment__featured_stickers">Featured</string>
|
||||
|
||||
<string name="MediaCountIndicatorButton__send">Send</string>
|
||||
|
||||
<string name="MediaReviewSelectedItem__tap_to_remove">Tap to remove</string>
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.lang.ref.WeakReference;
|
|||
/**
|
||||
* Maintains a weak reference to the an invalidate callback allowing future invalidation without memory leak risk.
|
||||
*/
|
||||
abstract class InvalidateableRenderer implements Renderer {
|
||||
public abstract class InvalidateableRenderer implements Renderer {
|
||||
|
||||
private WeakReference<RendererContext.Invalidate> invalidate = new WeakReference<>(null);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue